FEX/Source/Tests/UnitTestGenerator.cpp
Lioncash 75b2f226f6 General: Migrate over to fmt where possible
Migrates lingering instances of the old logger over to fmt where
applicable. This allows removing some of the old defines and functions.

The only remaining usages of the printf-based variant of the logger is
in Tests/LinuxSyscalls/Syscalls.cpp for the strace handling.
2021-11-23 12:51:57 -05:00

2433 lines
79 KiB
C++

/*
$info$
tags: Bin|UnitTestGenerator
desc: Brute forces generation of tests for x86/64, incomplete, unused right now
$end_info$
*/
#include "Common/ArgumentLoader.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/Context.h>
#include <array>
#include <cstdio>
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Debug/X86Tables.h>
#include <FEXCore/Utils/LogManager.h>
using FEXCore::X86Tables::OpToIndex;
constexpr std::array<std::pair<int16_t, int16_t>, 3> Disp8Ranges = {{
{static_cast<int16_t>(-16), 16},
{static_cast<int16_t>(-128), static_cast<int16_t>(-112)},
{96, 112},
}};
constexpr std::array<std::pair<int64_t, int64_t>, 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 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<uint32_t, FEXCore::X86Tables::TYPE_GROUP_P - FEXCore::X86Tables::TYPE_GROUP_6 + 1> 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<uint32_t, 4> 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<uint8_t> 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<std::function<void()>> 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 <uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<std::function<void(uint8_t)>> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<std::function<void()>> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<int32_t>(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<std::function<void(uint8_t)>> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<std::function<void()>> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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(FEXCore::Config::CreateMainLayer());
FEXCore::Config::AddLayer(std::make_unique<FEX::ArgLoader::ArgLoader>(argc, argv));
FEXCore::Config::AddLayer(FEXCore::Config::CreateEnvironmentLayer(envp));
FEXCore::Config::Load();
auto Args = FEX::ArgLoader::Get();
LOGMAN_THROW_A_FMT(!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;
}