/* $info$ tags: Bin|UnitTestGenerator desc: Brute forces generation of tests for x86/64, incomplete, unused right now $end_info$ */ #include "Common/ArgumentLoader.h" #include "Common/EnvironmentLoader.h" #include "Common/Config.h" #include #include #include #include #include #include constexpr std::array, 3> Disp8Ranges = {{ {static_cast(-16), 16}, {static_cast(-128), static_cast(-112)}, {96, 112}, }}; constexpr std::array, 1> Disp32Ranges = {{ {0, 32}, }}; int DumpThreshhold = (1 << 15); void MsgHandler(LogMan::DebugLevels Level, char const *Message) { const char *CharLevel{nullptr}; switch (Level) { case LogMan::NONE: CharLevel = "NONE"; break; case LogMan::ASSERT: CharLevel = "ASSERT"; break; case LogMan::ERROR: CharLevel = "ERROR"; break; case LogMan::DEBUG: CharLevel = "DEBUG"; break; case LogMan::INFO: CharLevel = "Info"; break; default: CharLevel = "???"; break; } printf("[%s] %s\n", CharLevel, Message); } void AssertHandler(char const *Message) { printf("[ASSERT] %s\n", Message); } uint32_t GetModRMMapping(uint32_t Register) { switch (Register) { case FEXCore::X86State::REG_RCX: Register = 0b001; break; case FEXCore::X86State::REG_RDX: Register = 0b010; break; case FEXCore::X86State::REG_RBX: Register = 0b011; break; case FEXCore::X86State::REG_RSP: Register = 0b100; break; case FEXCore::X86State::REG_RBP: Register = 0b101; break; case FEXCore::X86State::REG_RSI: Register = 0b110; break; case FEXCore::X86State::REG_RDI: Register = 0b111; break; default: return Register; break; // Default mapping } return Register; }; auto OpToIndex = [](uint8_t Op) constexpr -> uint8_t { switch (Op) { // Group 1 case 0x80: return 0; case 0x81: return 1; case 0x82: return 2; case 0x83: return 3; // Group 2 case 0xC0: return 0; case 0xC1: return 1; case 0xD0: return 2; case 0xD1: return 3; case 0xD2: return 4; case 0xD3: return 5; // Group 3 case 0xF6: return 0; case 0xF7: return 1; // Group 4 case 0xFE: return 0; // Group 5 case 0xFF: return 0; // Group 11 case 0xC6: return 0; case 0xC7: return 1; } return 0; }; auto PrimaryIndexToOp = [](uint16_t Op) constexpr -> uint32_t { #define OPD(group, prefix, Reg) ((((group) - FEXCore::X86Tables::TYPE_GROUP_1) << 6) | (prefix) << 3 | (Reg)) switch (Op & ~0b111) { // Group 1 case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 0): return 0x800000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 0): return 0x810000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x82), 0): return 0x820000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 0): return 0x830000 | (Op & 0b111); // Group 2 case OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 0): return 0xC00000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 0): return 0xC10000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 0): return 0xD00000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 0): return 0xD10000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 0): return 0xD20000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 0): return 0xD30000 | (Op & 0b111); // Group 3 case OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 0): return 0xF60000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 0): return 0xF70000 | (Op & 0b111); // Group 4 case OPD(FEXCore::X86Tables::TYPE_GROUP_4, OpToIndex(0xFE), 0): return 0xFE0000 | (Op & 0b111); // Group 5 case OPD(FEXCore::X86Tables::TYPE_GROUP_5, OpToIndex(0xFF), 0): return 0xFF0000 | (Op & 0b111); // Group 11 case OPD(FEXCore::X86Tables::TYPE_GROUP_11, OpToIndex(0xC6), 0): return 0xC60000 | (Op & 0b111); case OPD(FEXCore::X86Tables::TYPE_GROUP_11, OpToIndex(0xC7), 0): return 0xC70000 | (Op & 0b111); } #undef OPD return 0; }; auto SecondaryIndexToOp = [](uint16_t Op) constexpr -> uint32_t { constexpr std::array GroupToOp = { 0x000000, // 6 0x010000, // 7 0xBA0000, // 8 0xC70000, // 9 0xB90000, // 10 0x710000, // 12 (11 is part of the primary op table 0x720000, // 13 0x730000, // 14 0xAE0000, // 15 0x180000, // 16 0x780000, // 17 0x0D0000, // P }; constexpr std::array PrefixToOp = { 0, 0xF300, 0x6600, 0xF200, }; return GroupToOp[Op >> 5] | PrefixToOp[(Op >> 3) & 0b11] | (Op & 0b111); }; uint32_t GetModRMMappingXMM(uint32_t Register) { return Register - FEXCore::X86State::REG_XMM_0; }; static std::vector Code; static std::string Filepath; static std::string CurrentPrefix; static int Step{}; static int TimeSinceLastDump{}; void GenerateMove(uint32_t Register, uint64_t Literal) { Register = GetModRMMapping(Register); int Size = !!(Literal & (~0ULL << 32)) ? 8 : 4; uint8_t REX = 0x40 | (Size == 8 ? 0b1000 : 0); REX |= (Register & 0b1000) >> 3; Code.emplace_back(REX); // REX Code.emplace_back(0xB8 + (Register & 0b0111)); // MOV for (int i = 0; i < Size; ++i) Code.emplace_back(Literal >> (i * 8)); } void Dump(std::string const &NameSuffix = "") { { // Dump a HLT at the end of the code Code.emplace_back(0xF4); } printf("Size: %zd Inst: %d\n", Code.size(), TimeSinceLastDump); std::string Filename = Filepath + "/" + CurrentPrefix + "_" + std::to_string(Step) + "_" + NameSuffix + ".raw"; FILE *fp = fopen(Filename.c_str(), "wbe"); fwrite(&Code[0], 1, Code.size(), fp); fclose(fp); Code.clear(); Code.reserve(4096 * 128); ++Step; TimeSinceLastDump = 0; } void GeneratePrimaryTable() { using namespace FEXCore::X86Tables; int numInst {}; Step = 0; TimeSinceLastDump = 0; CurrentPrefix = "Primary"; bool AddressSizePrefix = false; auto DoNormalOps = [&](const char *NameSuffix, auto Inserter, std::optional> ModRMInserter, uint8_t REX = 0) { for (size_t OpIndex = 0; OpIndex < std::size(BaseOps); ++OpIndex) { auto &Op = BaseOps[OpIndex]; if (Op.Type == TYPE_INST) { if (Op.Flags & InstFlags::FLAGS_SETS_RIP || Op.Flags & InstFlags::FLAGS_BLOCK_END) { continue; } if (OpIndex == 0x8D) { // LEA // Special case LEA // LEA with source as a register is INVALID and causes test harness generation to fail continue; } // Need to specialize these if (Op.Flags & InstFlags::FLAGS_MEM_OFFSET || Op.Flags & InstFlags::FLAGS_DEBUG_MEM_ACCESS || Op.Flags & InstFlags::FLAGS_DEBUG) { continue; } if (Op.Flags & InstFlags::FLAGS_MODRM && !ModRMInserter.has_value()) { continue; } Inserter(); Code.push_back(OpIndex); if (Op.Flags & InstFlags::FLAGS_MODRM) { ModRMInserter.value()(); } if (Op.MoreBytes != 0) { uint32_t MoreBytes = Op.MoreBytes; if (REX & 0b1000) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_DISPLACE_SIZE_MUL_2) { MoreBytes <<= 1; } } if (AddressSizePrefix) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_DISPLACE_SIZE_DIV_2) { MoreBytes >>= 1; } } constexpr uint64_t Constant = 0xDEADBEEFBAD0DAD1ULL; for (uint32_t i = 0; i < MoreBytes; ++i) { Code.push_back(Constant >> (i * 8)); } } numInst++; #ifndef NDEBUG Op.NumUnitTestsGenerated++; #endif TimeSinceLastDump++; if (TimeSinceLastDump >= DumpThreshhold) { Dump(NameSuffix); } } } }; auto EmptyInserter = [](){}; DoNormalOps("", EmptyInserter, {}); Dump(); for (uint8_t REX = 0x40; REX < 0x50; ++REX) { auto Inserter = [REX]() { Code.push_back(REX); }; DoNormalOps("", Inserter, {}, REX); } Dump(); for (uint8_t Prefix = 0x64; Prefix < 0x66; ++Prefix) { auto Inserter = [Prefix]() { Code.push_back(Prefix); }; DoNormalOps("", Inserter, {}); } Dump(); AddressSizePrefix = true; for (uint8_t Prefix = 0x66; Prefix < 0x67; ++Prefix) { auto Inserter = [Prefix]() { Code.push_back(Prefix); }; DoNormalOps("", Inserter, {}); } AddressSizePrefix = false; for (uint8_t Prefix = 0x67; Prefix < 0x68; ++Prefix) { auto Inserter = [Prefix]() { Code.push_back(Prefix); }; DoNormalOps("", Inserter, {}); } Dump(); for (uint8_t REX = 0x40; REX < 0x50; ++REX) { for (uint8_t Prefix = 0x64; Prefix < 0x66; ++Prefix) { auto Inserter = [REX, Prefix]() { Code.push_back(Prefix); Code.push_back(REX); }; DoNormalOps("", Inserter, {}, REX); } } Dump(); // 00 = register direct // - rm = 100 = SIB - Supported // - rm = 101 = disp32 // 01 = Register direct + displacement8 // - rm = 100 = SIB + disp8 - Supported // 10 = register direct + displacement32 // - rm = 100 = SIB + disp32 - Supported { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; auto Inserter = [ModRM]() { Code.push_back(ModRM); }; DoNormalOps("ModRM", EmptyInserter, Inserter); } } Dump("ModRM"); } // For register direct we need to load a memory region in to a register // Memory region is at [0xe000'0000, 0xf000'0000] // Drop ourselves right in the middle for testing { uint8_t ModRM_mod = 0b00; const std::vector Registers = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, // FEXCore::X86State::REG_RBP, // mod=00 = RIP relative addressing // FEXCore::X86State::REG_RSP, // SIB FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // mod=00 = RIP RELATIVE. Ignores bit in REX // FEXCore::X86State::REG_R13, // SIB. Ignores bit in REX FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto RM : Registers) { uint8_t ModRM_rm = GetModRMMapping(RM); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | (ModRM_rm & 0b111); auto PreInserter = [ModRM_rm, RM]() { GenerateMove(RM, 0xe000'0000 + 0x800'0000); if (ModRM_rm & 0b1000) { // We need REX for this uint8_t REX = 0x40; REX |= (ModRM_rm & 0b1000) >> 3; Code.emplace_back(REX); // REX } }; auto Inserter = [ModRM]() { // Before we do anything, set this register to our memory region Code.push_back(ModRM); }; DoNormalOps("ModRM", PreInserter, Inserter); } } Dump("ModRM"); } // For register indirect we need to load a memory region in to a register // Memory region is at [0xe000'0000, 0xf000'0000] // Drop ourselves right in the middle for testing // Displacement is only 8bit here { uint8_t ModRM_mod = 0b01; const std::vector Registers = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; for (auto DispRange : Disp8Ranges) { for(int16_t disp8 = DispRange.first; disp8 <= DispRange.second; disp8 += 16) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto RM : Registers) { uint8_t ModRM_rm = GetModRMMapping(RM); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | (ModRM_rm & 0b111); auto PreInserter = [ModRM_rm, RM]() { GenerateMove(RM, 0xe000'0000 + 0x800'0000); if (ModRM_rm & 0b1000) { // We need REX for this uint8_t REX = 0x40; REX |= (ModRM_rm & 0b1000) >> 3; Code.emplace_back(REX); // REX } }; auto Inserter = [ModRM, disp8]() { // Before we do anything, set this register to our memory region Code.push_back(ModRM); // Disp8 bit follows ModRM Code.push_back(disp8); }; DoNormalOps("ModRM", PreInserter, Inserter); } } } } Dump("ModRM"); } { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, // FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_RSP, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, FEXCore::X86State::REG_R12, // FEXCore::X86State::REG_R13, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; uint8_t ModRM_mod = 0b00; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB }; DoNormalOps("ModRM_SIB", PreInserter, Inserter, REX); } } } } Dump("ModRM_SIB"); } { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b01; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto DispRange : Disp8Ranges) { for(int16_t disp8 = DispRange.first; disp8 <= DispRange.second; disp8 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, disp8]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB Code.push_back(disp8); }; DoNormalOps("ModRM_SIB_disp8", PreInserter, Inserter, REX); } } } } } } Dump("ModRM_SIB_disp8"); } { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b10; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto DispRange : Disp32Ranges) { for(int64_t disp32 = DispRange.first; disp32 <= DispRange.second; disp32 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0x2'0000'0000 + 0x0'8000'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, disp32]() { int32_t disp = disp32; Code.push_back(ModRM); Code.push_back(SIB); // SIB // Disp32 bit follows SIB for (int i = 0; i < 4; ++i) Code.push_back(disp >> (i * 8)); }; DoNormalOps("ModRM_SIB_disp32", PreInserter, Inserter, REX); } } } } } } Dump("ModRM_SIB_disp32"); } // For register indirect we need to load a memory region in to a register // Memory region is at [0xe000'0000, 0xf000'0000] // Additional 4GB region is at [0x2'0000'0000, 0x3'0000'1000) // Drop ourselves right in the middle for testing // Displacement is 32bit here { uint8_t ModRM_mod = 0b10; const std::vector Registers = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; for (auto DispRange : Disp32Ranges) { for(int64_t disp32 = DispRange.first; disp32 <= DispRange.second; disp32 += 16) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto RM : Registers) { uint8_t ModRM_rm = GetModRMMapping(RM); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | (ModRM_rm & 0b111); auto PreInserter = [ModRM_rm, RM]() { GenerateMove(RM, 0x2'0000'0000 + 0x0'8000'0000); if (ModRM_rm & 0b1000) { // We need REX for this uint8_t REX = 0x40; REX |= (ModRM_rm & 0b1000) >> 3; Code.emplace_back(REX); // REX } }; auto Inserter = [ModRM, disp32]() { // Before we do anything, set this register to our memory region Code.push_back(ModRM); // Disp32 bit follows ModRM for (int i = 0; i < 4; ++i) Code.push_back(disp32 >> (i * 8)); }; DoNormalOps("ModRM", PreInserter, Inserter); } } } } Dump("ModRM"); } for (uint8_t REX = 0x40; REX < 0x50; ++REX) { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; auto REXInserter = [REX]() { Code.push_back(REX); }; auto Inserter = [ModRM]() { Code.push_back(ModRM); }; DoNormalOps("ModRM", REXInserter, Inserter, REX); } } } Dump("ModRM"); printf("NumInsts: %d\n", numInst); } void GeneratePrimaryGroupTable() { using namespace FEXCore::X86Tables; int numInst {}; Step = 0; TimeSinceLastDump = 0; CurrentPrefix = "PrimaryGroup"; auto DoNormalOps = [&](const char *NameSuffix, auto SkipCheck, auto Inserter, auto &Table, std::optional> ModRMInserter, uint8_t REX = 0) { for (size_t OpIndex = 0; OpIndex < std::size(Table); ++OpIndex) { auto &Op = Table[OpIndex]; if (Op.Type == TYPE_INST) { if (Op.Flags & InstFlags::FLAGS_SETS_RIP || Op.Flags & InstFlags::FLAGS_BLOCK_END) { continue; } if (SkipCheck(Op)) { continue; } // Need to specialize these if (Op.Flags & InstFlags::FLAGS_MEM_OFFSET || Op.Flags & InstFlags::FLAGS_DEBUG_MEM_ACCESS || Op.Flags & InstFlags::FLAGS_DEBUG) { continue; } if (Op.Flags & InstFlags::FLAGS_MODRM && !ModRMInserter.has_value()) { continue; } #ifndef NDEBUG if (Op.DebugInfo.DebugFlags & FEXCore::X86Tables::X86InstDebugInfo::FLAGS_DIVIDE) { continue; } #endif Inserter(); uint32_t HexOp = PrimaryIndexToOp(OpIndex); Code.push_back(HexOp >> 16); // Op selection is from the reg field of modrm if (Op.Flags & InstFlags::FLAGS_MODRM) { ModRMInserter.value()(HexOp & 0b111); } if (Op.MoreBytes != 0) { uint32_t MoreBytes = Op.MoreBytes; if (REX & 0b1000) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_DISPLACE_SIZE_MUL_2) { MoreBytes <<= 1; } } constexpr uint64_t Constant = 0xDEADBEEFBAD0DAD1ULL; for (uint32_t i = 0; i < MoreBytes; ++i) { Code.push_back(Constant >> (i * 8)); } } numInst++; #ifndef NDEBUG Op.NumUnitTestsGenerated++; #endif TimeSinceLastDump++; if (TimeSinceLastDump >= DumpThreshhold) { Dump(NameSuffix); } } } }; auto EmptyInserter = [](){}; // 00 = register direct // - rm = 100 = SIB - Supported // - rm = 101 = disp32 // 01 = Register direct + displacement8 // - rm = 100 = SIB + disp8 - Supported // 10 = register direct + displacement32 // - rm = 100 = SIB + disp32 - Supported { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; auto Inserter = [ModRM](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_MEM_ONLY) { return true; } return false; }; DoNormalOps("ModRM", SkipCheck, EmptyInserter, PrimaryInstGroupOps, Inserter); } Dump("ModRM"); } { auto SIBFunction = [&DoNormalOps](const char *Name, auto &Table) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, // FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_RSP, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, FEXCore::X86State::REG_R12, // FEXCore::X86State::REG_R13, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; uint8_t ModRM_mod = 0b00; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); Code.push_back(SIB); // SIB }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { return true; } return false; }; DoNormalOps(Name, SkipCheck, PreInserter, Table, Inserter, REX); } } } Dump(Name); }; SIBFunction("ModRM_SIB", PrimaryInstGroupOps); } { auto SIBFunction = [&DoNormalOps](const char *Name, auto &Table) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b01; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (auto DispRange : Disp8Ranges) { for(int16_t disp8 = DispRange.first; disp8 <= DispRange.second; disp8 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, &disp8](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); Code.push_back(SIB); // SIB Code.push_back(disp8); }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { return true; } return false; }; DoNormalOps(Name, SkipCheck, PreInserter, Table, Inserter, REX); } } } } } Dump(Name); }; SIBFunction("ModRM_SIB8", PrimaryInstGroupOps); } { auto SIBFunction = [&DoNormalOps](const char *Name, auto &Table) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b10; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (auto DispRange : Disp32Ranges) { for(int64_t disp32 = DispRange.first; disp32 <= DispRange.second; disp32 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0x2'0000'0000 + 0x0'8000'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, disp32](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); Code.push_back(SIB); // SIB // Disp32 bit follows SIB for (int i = 0; i < 4; ++i) Code.push_back(disp32 >> (i * 8)); }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { return true; } return false; }; DoNormalOps(Name, SkipCheck, PreInserter, Table, Inserter, REX); } } } } } Dump(Name); }; SIBFunction("ModRM_SIB32", PrimaryInstGroupOps); } printf("Primary Group NumInsts: %d\n", numInst); } void GenerateSecondaryTable() { using namespace FEXCore::X86Tables; int numInst {}; Step = 0; TimeSinceLastDump = 0; CurrentPrefix = "Secondary"; auto DoNormalOps = [&](const char *NameSuffix, auto SkipCheck, auto Inserter, std::optional> ModRMInserter, uint8_t REX = 0) { for (size_t OpIndex = 0; OpIndex < std::size(SecondBaseOps); ++OpIndex) { auto &Op = SecondBaseOps[OpIndex]; if (Op.Type == TYPE_INST) { if (Op.Flags & InstFlags::FLAGS_SETS_RIP || Op.Flags & InstFlags::FLAGS_BLOCK_END) { continue; } if (SkipCheck(Op)) { continue; } // Need to specialize these if (Op.Flags & InstFlags::FLAGS_MEM_OFFSET || Op.Flags & InstFlags::FLAGS_DEBUG_MEM_ACCESS || Op.Flags & InstFlags::FLAGS_DEBUG) { continue; } if (Op.Flags & InstFlags::FLAGS_MODRM && !ModRMInserter.has_value()) { continue; } Inserter(); Code.push_back(0x0F); // Escape op Code.push_back(OpIndex); if (Op.Flags & InstFlags::FLAGS_MODRM) { ModRMInserter.value()(); } if (Op.MoreBytes != 0) { uint32_t MoreBytes = Op.MoreBytes; if (REX & 0b1000) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_DISPLACE_SIZE_MUL_2) { MoreBytes <<= 1; } } constexpr uint64_t Constant = 0xDEADBEEFBAD0DAD1ULL; for (uint32_t i = 0; i < MoreBytes; ++i) { Code.push_back(Constant >> (i * 8)); } } numInst++; #ifndef NDEBUG Op.NumUnitTestsGenerated++; #endif TimeSinceLastDump++; if (TimeSinceLastDump >= DumpThreshhold) { Dump(NameSuffix); } } } }; auto EmptyInserter = [](){}; auto EmptySkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { return false; }; DoNormalOps("", EmptySkipCheck, EmptyInserter, {}); Dump(); // 00 = register direct // - rm = 100 = SIB - Supported // - rm = 101 = disp32 // 01 = Register direct + displacement8 // - rm = 100 = SIB + disp8 - Supported // 10 = register direct + displacement32 // - rm = 100 = SIB + disp32 - Supported { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; auto Inserter = [ModRM]() { Code.push_back(ModRM); }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_MEM_ONLY) { return true; } return false; }; DoNormalOps("ModRM", SkipCheck, EmptyInserter, Inserter); } } Dump("ModRM"); } { auto SIBFunction = [&DoNormalOps](const char *Name) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, // FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_RSP, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, FEXCore::X86State::REG_R12, // FEXCore::X86State::REG_R13, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; uint8_t ModRM_mod = 0b00; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { return true; } return false; }; DoNormalOps(Name, SkipCheck, PreInserter, Inserter, REX); } } } } Dump(Name); }; SIBFunction("ModRM_SIB"); } { auto SIBFunction = [&DoNormalOps](const char *Name) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b01; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto DispRange : Disp8Ranges) { for(int16_t disp8 = DispRange.first; disp8 <= DispRange.second; disp8 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, disp8]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB Code.push_back(disp8); }; auto SkipRegOnly = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM && Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { // Skip if the modrm source is reg only return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS) { return true; } return false; }; DoNormalOps(Name, SkipRegOnly, PreInserter, Inserter, REX); } } } } } } Dump(Name); }; SIBFunction("ModRM_SIB8"); } { auto SIBFunction = [&DoNormalOps](const char *Name) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b10; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto DispRange : Disp32Ranges) { for(int64_t disp32 = DispRange.first; disp32 <= DispRange.second; disp32 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0x2'0000'0000 + 0x0'8000'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, disp32]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB int32_t udisp32 = static_cast(disp32); // Disp32 bit follows SIB for (int i = 0; i < 4; ++i) { Code.push_back(udisp32 & 0xFF); udisp32 >>= 8; } }; auto SkipRegOnly = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM && Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { // Skip if the modrm source is reg only return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS) { return true; } return false; }; DoNormalOps(Name, SkipRegOnly, PreInserter, Inserter, REX); } } } } } } Dump(Name); }; SIBFunction("ModRM_SIB32"); } printf("Secondary NumInsts: %d\n", numInst); } void GenerateSecondaryGroupTable() { using namespace FEXCore::X86Tables; int numInst {}; Step = 0; TimeSinceLastDump = 0; CurrentPrefix = "SecondaryGroup"; auto DoNormalOps = [&](const char *NameSuffix, auto SkipCheck, auto Inserter, std::optional> ModRMInserter, uint8_t REX = 0) { for (size_t OpIndex = 0; OpIndex < std::size(SecondInstGroupOps); ++OpIndex) { auto &Op = SecondInstGroupOps[OpIndex]; if (Op.Type == TYPE_INST) { if (Op.Flags & InstFlags::FLAGS_SETS_RIP || Op.Flags & InstFlags::FLAGS_BLOCK_END) { continue; } #ifndef NDEBUG if (Op.DebugInfo.DebugFlags & FEXCore::X86Tables::X86InstDebugInfo::FLAGS_DEBUG) { continue; } #endif if (SkipCheck(Op)) { continue; } // Need to specialize these if (Op.Flags & InstFlags::FLAGS_MEM_OFFSET || Op.Flags & InstFlags::FLAGS_DEBUG_MEM_ACCESS || Op.Flags & InstFlags::FLAGS_DEBUG) { continue; } if (Op.Flags & InstFlags::FLAGS_MODRM && !ModRMInserter.has_value()) { continue; } Inserter(); uint32_t HexOp = SecondaryIndexToOp(OpIndex); uint32_t SecondaryEscapeOp = (HexOp >> 8) & 0xFF; if (SecondaryEscapeOp != 0) Code.push_back(SecondaryEscapeOp); // Secondary escape op Code.push_back(0x0F); // Escape op Code.push_back((HexOp >> 16) & 0xFF); // Op selection is from the reg field of modrm if (Op.Flags & InstFlags::FLAGS_MODRM) { ModRMInserter.value()(HexOp & 0b111); } if (Op.MoreBytes != 0) { uint32_t MoreBytes = Op.MoreBytes; if (REX & 0b1000) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_DISPLACE_SIZE_MUL_2) { MoreBytes <<= 1; } } constexpr uint64_t Constant = 0xDEADBEEFBAD0DAD1ULL; for (uint32_t i = 0; i < MoreBytes; ++i) { Code.push_back(Constant >> (i * 8)); } } numInst++; #ifndef NDEBUG Op.NumUnitTestsGenerated++; #endif TimeSinceLastDump++; if (TimeSinceLastDump >= DumpThreshhold) { Dump(NameSuffix); } } } }; auto EmptyInserter = [](){}; // 00 = register direct // - rm = 100 = SIB - Supported // - rm = 101 = disp32 // 01 = Register direct + displacement8 // - rm = 100 = SIB + disp8 - Supported // 10 = register direct + displacement32 // - rm = 100 = SIB + disp32 - Supported { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; auto Inserter = [ModRM](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_MEM_ONLY) { return true; } return false; }; DoNormalOps("ModRM", SkipCheck, EmptyInserter, Inserter); } Dump("ModRM"); } { auto SIBFunction = [&DoNormalOps](const char *Name) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, // FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_RSP, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, FEXCore::X86State::REG_R12, // FEXCore::X86State::REG_R13, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; uint8_t ModRM_mod = 0b00; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); Code.push_back(SIB); // SIB }; auto SkipCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { return true; } return false; }; DoNormalOps(Name, SkipCheck, PreInserter, Inserter, REX); } } } Dump(Name); }; SIBFunction("ModRM_SIB"); } { auto SIBFunction = [&DoNormalOps](const char *Name) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b01; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (auto DispRange : Disp8Ranges) { for(int16_t disp8 = DispRange.first; disp8 <= DispRange.second; disp8 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, disp8](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); Code.push_back(SIB); // SIB Code.push_back(disp8); }; auto SkipRegOnly = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM && Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { // Skip if the modrm source is reg only return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS) { return true; } return false; }; DoNormalOps(Name, SkipRegOnly, PreInserter, Inserter, REX); } } } } } Dump(Name); }; SIBFunction("ModRM_SIB8"); } { auto SIBFunction = [&DoNormalOps](const char *Name) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b10; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (auto DispRange : Disp32Ranges) { for(int64_t disp32 = DispRange.first; disp32 <= DispRange.second; disp32 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (0 << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase]() { GenerateMove(RegBase, 0x2'0000'0000 + 0x0'8000'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX }; auto Inserter = [ModRM, &SIB, disp32](uint8_t reg_field) { Code.push_back(ModRM | (reg_field << 3)); Code.push_back(SIB); // SIB // Disp32 bit follows SIB for (int i = 0; i < 4; ++i) Code.push_back(disp32 >> (i * 8)); }; auto SkipRegOnly = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM && Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY) { // Skip if the modrm source is reg only return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS) { return true; } return false; }; DoNormalOps(Name, SkipRegOnly, PreInserter, Inserter, REX); } } } } } Dump(Name); }; SIBFunction("ModRM_SIB32"); } printf("Secondary Group NumInsts: %d\n", numInst); } void GenerateSSEInstructions() { using namespace FEXCore::X86Tables; int numInst {}; Step = 0; TimeSinceLastDump = 0; CurrentPrefix = "SSE"; bool AddressSizePrefix = false; auto DoNormalOps = [&](const char *NameSuffix, auto SkipCheck, auto Inserter, auto &Table, std::optional> ModRMInserter, uint8_t REX = 0) { for (size_t OpIndex = 0; OpIndex < std::size(Table); ++OpIndex) { auto &Op = Table[OpIndex]; if (Op.Type == TYPE_INST) { if (Op.Flags & InstFlags::FLAGS_SETS_RIP || Op.Flags & InstFlags::FLAGS_BLOCK_END) { continue; } if (!(Op.Flags & InstFlags::FLAGS_XMM_FLAGS)) { continue; } if (SkipCheck(Op)) { continue; } // Need to specialize these if (Op.Flags & InstFlags::FLAGS_MEM_OFFSET || Op.Flags & InstFlags::FLAGS_DEBUG_MEM_ACCESS || Op.Flags & InstFlags::FLAGS_DEBUG) { continue; } if (Op.Flags & InstFlags::FLAGS_MODRM && !ModRMInserter.has_value()) { continue; } Inserter(); Code.push_back(0x0F); // Need to escape to get in to this table Code.push_back(OpIndex); if (Op.Flags & InstFlags::FLAGS_MODRM) { ModRMInserter.value()(); } if (Op.MoreBytes != 0) { uint32_t MoreBytes = Op.MoreBytes; if (REX & 0b1000) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_DISPLACE_SIZE_MUL_2) { MoreBytes <<= 1; } } if (AddressSizePrefix) { if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_DISPLACE_SIZE_DIV_2) { MoreBytes >>= 1; } } constexpr uint64_t Constant = 0xDEADBEEFBAD0DAD1ULL; for (uint32_t i = 0; i < MoreBytes; ++i) { Code.push_back(Constant >> (i * 8)); } } numInst++; #ifndef NDEBUG Op.NumUnitTestsGenerated++; #endif TimeSinceLastDump++; if (TimeSinceLastDump >= DumpThreshhold) { Dump(NameSuffix); } } } }; auto EmptyInserter = [](){}; auto SkipMemOnlyCheck = [](FEXCore::X86Tables::X86InstInfo const &Op) { if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM)) { return true; } if (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_MEM_ONLY) { return true; } return false; }; // 00 = register direct // - rm = 100 = SIB - Supported // - rm = 101 = disp32 // 01 = Register direct + displacement8 // - rm = 100 = SIB + disp8 - Supported // 10 = register direct + displacement32 // - rm = 100 = SIB + disp32 - Supported { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; auto Inserter = [ModRM]() { Code.push_back(ModRM); }; DoNormalOps("ModRM", SkipMemOnlyCheck, EmptyInserter, SecondBaseOps, Inserter); } } Dump("ModRM"); } { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; auto Inserter = [ModRM]() { Code.push_back(ModRM); }; auto REPInserter = []() { Code.push_back(0xF3); }; DoNormalOps("ModRM_REP", SkipMemOnlyCheck, REPInserter, RepModOps, Inserter); } } Dump("ModRM_REP"); } { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; auto Inserter = [ModRM]() { Code.push_back(ModRM); }; auto REPInserter = []() { Code.push_back(0xF2); }; DoNormalOps("ModRM_REPNE", SkipMemOnlyCheck, REPInserter, RepNEModOps, Inserter); } } Dump("ModRM_REPNE"); } { uint8_t ModRM_mod = 0b11; for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (uint8_t ModRM_rm = 0; ModRM_rm < 8; ++ModRM_rm) { uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; auto Inserter = [ModRM]() { Code.push_back(ModRM); }; auto OpSizeInserter = []() { Code.push_back(0x66); }; DoNormalOps("ModRM_OpSize", SkipMemOnlyCheck, OpSizeInserter, OpSizeModOps, Inserter); } } Dump("ModRM_OpSize"); } { auto SIBFunction = [&DoNormalOps](uint8_t Prefix, const char *Name, auto &Table) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, // FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_RSP, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, FEXCore::X86State::REG_R12, // FEXCore::X86State::REG_R13, // ModRM = 0b00 = base = 0 FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; uint8_t ModRM_mod = 0b00; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase, Prefix]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX if (Prefix != 0) { Code.emplace_back(Prefix); } }; auto Inserter = [ModRM, &SIB]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB }; auto SkipRegOnly = [](FEXCore::X86Tables::X86InstInfo const &Op) { // Skip if the modrm source is reg only return Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM && Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY; }; DoNormalOps(Name, SkipRegOnly, PreInserter, Table, Inserter, REX); } } } } Dump(Name); }; SIBFunction(0, "ModRM_SIB", SecondBaseOps); SIBFunction(0xF3, "ModRM_REP_SIB", RepModOps); SIBFunction(0xF2, "ModRM_REPNE_SIB", RepNEModOps); SIBFunction(0x66, "ModRM_OpSize_SIB", OpSizeModOps); } { auto SIBFunction = [&DoNormalOps](uint8_t Prefix, const char *Name, auto &Table) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b01; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto DispRange : Disp8Ranges) { for(int16_t disp8 = DispRange.first; disp8 <= DispRange.second; disp8 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase, Prefix]() { GenerateMove(RegBase, 0xe000'0000 + 0x800'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX if (Prefix != 0) { Code.emplace_back(Prefix); } }; auto Inserter = [ModRM, &SIB, disp8]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB Code.push_back(disp8); }; auto SkipRegOnly = [](FEXCore::X86Tables::X86InstInfo const &Op) { // Skip if the modrm source is reg only return Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM && Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY; }; DoNormalOps(Name, SkipRegOnly, PreInserter, Table, Inserter, REX); } } } } } } Dump(Name); }; SIBFunction(0, "ModRM_SIB8", SecondBaseOps); SIBFunction(0xF3, "ModRM_REP_SIB8", RepModOps); SIBFunction(0xF2, "ModRM_REPNE_SIB8", RepNEModOps); SIBFunction(0x66, "ModRM_OpSize_SIB8", OpSizeModOps); } { auto SIBFunction = [&DoNormalOps](uint8_t Prefix, const char *Name, auto &Table) { const std::vector RegistersIndex = { FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP, // FEXCore::X86State::REG_RSP, // RSP = Scale*Index = 0 FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9, FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R11, // FEXCore::X86State::REG_R12, // = 0 FEXCore::X86State::REG_R13, FEXCore::X86State::REG_R14, FEXCore::X86State::REG_R15, }; const std::vector RegistersBase = { FEXCore::X86State::REG_RBP, // ModRM = 0b00 = base = 0 }; uint8_t ModRM_mod = 0b10; uint8_t ModRM_rm = 0b100; for (uint32_t RegIndex : RegistersIndex) { for (uint32_t RegBase : RegistersBase) { for (uint8_t scale = 0; scale < 1; ++scale) { for (uint8_t ModRM_reg = 0; ModRM_reg < 8; ++ModRM_reg) { for (auto DispRange : Disp32Ranges) { for(int64_t disp32 = DispRange.first; disp32 <= DispRange.second; disp32 += 16) { if (RegIndex == RegBase) { // Skip these continue; } uint8_t IndexReg = GetModRMMapping(RegIndex); uint8_t BaseReg = GetModRMMapping(RegBase); uint8_t ModRM = (ModRM_mod << 6) | (ModRM_reg << 3) | ModRM_rm; uint8_t REX = 0x40; REX |= (IndexReg & 0b1000) >> 2; REX |= (BaseReg & 0b1000) >> 3; uint8_t index = IndexReg & 0b111; uint8_t base = BaseReg & 0b111; uint8_t SIB = (scale << 6) | (index << 3) | base; auto PreInserter = [REX, RegIndex, RegBase, Prefix]() { GenerateMove(RegBase, 0x2'0000'0000 + 0x0'8000'0000); GenerateMove(RegIndex, 16); // Just ensure 128bit alignment Code.emplace_back(REX); // REX if (Prefix != 0) { Code.emplace_back(Prefix); } }; auto Inserter = [ModRM, &SIB, disp32]() { Code.push_back(ModRM); Code.push_back(SIB); // SIB // Disp32 bit follows SIB for (int i = 0; i < 4; ++i) Code.push_back(disp32 >> (i * 8)); }; auto SkipRegOnly = [](FEXCore::X86Tables::X86InstInfo const &Op) { if ((Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM) && (Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_REG_ONLY)) { // Skip if the modrm source is reg only return true; } if (!(Op.Flags & FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS)) { return true; } return false; }; DoNormalOps(Name, SkipRegOnly, PreInserter, Table, Inserter, REX); } } } } } } Dump(Name); }; SIBFunction(0, "ModRM_SIB32", SecondBaseOps); SIBFunction(0xF3, "ModRM_REP_SIB32", RepModOps); SIBFunction(0xF2, "ModRM_REPNE_SIB32", RepNEModOps); SIBFunction(0x66, "ModRM_OpSize_SIB32", OpSizeModOps); } printf("SSE NumInsts: %d\n", numInst); } int main(int argc, char **argv, char **const envp) { LogMan::Throw::InstallHandler(AssertHandler); LogMan::Msg::InstallHandler(MsgHandler); FEXCore::Config::Initialize(); FEXCore::Config::AddLayer(std::make_unique()); FEXCore::Config::AddLayer(std::make_unique(argc, argv)); FEXCore::Config::AddLayer(std::make_unique(envp)); FEXCore::Config::Load(); auto Args = FEX::ArgLoader::Get(); LOGMAN_THROW_A(!Args.empty(), "Not enough arguments"); FEXCore::Context::InitializeStaticTables(FEXCore::Context::MODE_64BIT); Code.reserve(4096*128); Filepath = Args[0]; GeneratePrimaryTable(); GeneratePrimaryGroupTable(); GenerateSecondaryTable(); GenerateSecondaryGroupTable(); GenerateSSEInstructions(); #ifndef NDEBUG if (Args.size() > 1) { using namespace FEXCore::X86Tables; auto DumpTable = [](std::string const &Filepath, std::string const &TableName, auto &Table) { std::string Filename = Filepath + "/" + TableName + ".csv"; FILE *fp = fopen(Filename.c_str(), "wbe"); fprintf(fp, "HEX, Name, Num Times compiled\n"); for (size_t OpIndex = 0; OpIndex < std::size(Table); ++OpIndex) { auto &Op = Table[OpIndex]; if (Op.Type == TYPE_INST) { fprintf(fp, "0x%zx, %s, %d\n", OpIndex, Op.Name, Op.NumUnitTestsGenerated); } } fclose(fp); }; DumpTable(Args[1], "Primary", BaseOps); DumpTable(Args[1], "Secondary", SecondBaseOps); DumpTable(Args[1], "Secondary_REP", RepModOps); DumpTable(Args[1], "Secondary_REPNE", RepNEModOps); DumpTable(Args[1], "Secondary_OpSize", OpSizeModOps); DumpTable(Args[1], "Primary_Groups", PrimaryInstGroupOps); DumpTable(Args[1], "Secondary_Groups", SecondInstGroupOps); DumpTable(Args[1], "SecondaryModRM", SecondModRMTableOps); } #endif return 0; }