Files
FEX-Emu--FEX/include/FEXCore/Debug/X86Tables.h
T
Ryan Houdek 5db8de625a Adds a few new instructions to the X86Tables
I managed to hit these when messing with Multiblock.
2020-03-06 07:55:37 +02:00

411 lines
12 KiB
C++

#pragma once
#include <cstdint>
#include <cstring>
#include <type_traits>
namespace FEXCore::IR {
///< Forward declaration of OpDispatchBuilder
class OpDispatchBuilder;
}
namespace FEXCore::X86Tables {
///< Forward declaration of X86InstInfo
struct X86InstInfo;
namespace DecodeFlags {
constexpr uint32_t FLAG_OPERAND_SIZE = (1 << 0);
constexpr uint32_t FLAG_ADDRESS_SIZE = (1 << 1);
constexpr uint32_t FLAG_LOCK = (1 << 2);
constexpr uint32_t FLAG_LEGACY_PREFIX = (1 << 3);
constexpr uint32_t FLAG_REX_PREFIX = (1 << 4);
constexpr uint32_t FLAG_MODRM_PRESENT = (1 << 5);
constexpr uint32_t FLAG_SIB_PRESENT = (1 << 6);
constexpr uint32_t FLAG_REX_WIDENING = (1 << 7);
constexpr uint32_t FLAG_REX_XGPR_B = (1 << 8);
constexpr uint32_t FLAG_REX_XGPR_X = (1 << 9);
constexpr uint32_t FLAG_REX_XGPR_R = (1 << 10);
constexpr uint32_t FLAG_FS_PREFIX = (1 << 11);
constexpr uint32_t FLAG_GS_PREFIX = (1 << 12);
constexpr uint32_t FLAG_REP_PREFIX = (1 << 13);
constexpr uint32_t FLAG_REPNE_PREFIX = (1 << 14);
// Size flags
constexpr uint32_t FLAG_SIZE_DST_OFF = 15;
constexpr uint32_t FLAG_SIZE_SRC_OFF = FLAG_SIZE_DST_OFF + 3;
constexpr uint32_t SIZE_MASK = 0b111;
constexpr uint32_t SIZE_DEF = 0b000; // This should be invalid past decoding
constexpr uint32_t SIZE_8BIT = 0b001;
constexpr uint32_t SIZE_16BIT = 0b010;
constexpr uint32_t SIZE_32BIT = 0b011;
constexpr uint32_t SIZE_64BIT = 0b100;
constexpr uint32_t SIZE_128BIT = 0b101;
constexpr uint32_t SIZE_256BIT = 0b110;
inline uint32_t GetSizeDstFlags(uint32_t Flags) { return (Flags >> FLAG_SIZE_DST_OFF) & SIZE_MASK; }
inline uint32_t GetSizeSrcFlags(uint32_t Flags) { return (Flags >> FLAG_SIZE_SRC_OFF) & SIZE_MASK; }
inline uint32_t GenSizeDstSize(uint32_t Size) { return Size << FLAG_SIZE_DST_OFF; }
inline uint32_t GenSizeSrcSize(uint32_t Size) { return Size << FLAG_SIZE_SRC_OFF; }
}
union DecodedOperand {
enum {
TYPE_NONE,
TYPE_GPR,
TYPE_GPR_DIRECT,
TYPE_GPR_INDIRECT,
TYPE_RIP_RELATIVE,
TYPE_LITERAL,
TYPE_SIB,
};
struct {
uint8_t Type;
} TypeNone;
struct {
uint8_t Type;
bool HighBits;
uint8_t GPR;
} TypeGPR;
struct {
uint8_t Type;
uint8_t GPR;
int32_t Displacement;
} TypeGPRIndirect;
struct {
uint8_t Type;
int32_t Literal;
} TypeRIPLiteral;
struct {
uint8_t Type;
uint8_t Size;
uint64_t Literal;
} TypeLiteral;
struct {
uint8_t Type;
uint8_t Index; // ~0 invalid
uint8_t Base; // ~0 invalid
uint32_t Scale : 8;
int32_t Offset;
} TypeSIB;
};
struct DecodedInst {
uint64_t PC;
uint16_t OP;
uint32_t Flags;
uint8_t ModRM;
uint8_t SIB;
uint8_t InstSize;
uint8_t LastEscapePrefix;
bool DecodedModRM;
bool DecodedSIB;
DecodedOperand Dest;
DecodedOperand Src1;
DecodedOperand Src2;
// Constains the dispatcher handler pointer
X86InstInfo const* TableInfo;
};
union ModRMDecoded {
uint8_t Hex{};
struct {
uint8_t rm : 3;
uint8_t reg : 3;
uint8_t mod : 2;
};
};
union SIBDecoded {
uint8_t Hex{};
struct {
uint8_t base : 3;
uint8_t index : 3;
uint8_t scale : 2;
};
};
enum InstType {
TYPE_UNKNOWN,
TYPE_LEGACY_PREFIX,
TYPE_PREFIX,
TYPE_REX_PREFIX,
TYPE_SECONDARY_TABLE_PREFIX,
TYPE_X87_TABLE_PREFIX,
TYPE_VEX_TABLE_PREFIX,
TYPE_XOP_TABLE_PREFIX,
TYPE_INST,
TYPE_X87 = TYPE_INST,
TYPE_INVALID,
TYPE_COPY_OTHER,
// Must be in order
// Groups 1, 1a, 2, 3, 4, 5, 11 are for the primary op table
// Groups 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, p are for the secondary op table
TYPE_GROUP_1,
TYPE_GROUP_1A,
TYPE_GROUP_2,
TYPE_GROUP_3,
TYPE_GROUP_4,
TYPE_GROUP_5,
TYPE_GROUP_11,
// Must be in order
// Groups 6-p Are for the secondary op table
TYPE_GROUP_6,
TYPE_GROUP_7,
TYPE_GROUP_8,
TYPE_GROUP_9,
TYPE_GROUP_10,
TYPE_GROUP_12,
TYPE_GROUP_13,
TYPE_GROUP_14,
TYPE_GROUP_15,
TYPE_GROUP_16,
TYPE_GROUP_17,
TYPE_GROUP_P,
// The secondary op extension table allows further extensions
// Group 7 allows additional extensions to this table
TYPE_SECOND_GROUP_MODRM,
TYPE_VEX_GROUP_12,
TYPE_VEX_GROUP_13,
TYPE_VEX_GROUP_14,
TYPE_VEX_GROUP_15,
TYPE_VEX_GROUP_17,
TYPE_GROUP_EVEX,
// Just to make grepping easier
TYPE_3DNOW_TABLE = TYPE_INVALID,
TYPE_3DNOW_INST = TYPE_INVALID,
// Exists in the table but isn't decoded correctly
TYPE_UNDEC = TYPE_INVALID,
TYPE_MMX = TYPE_INVALID,
TYPE_PRIV = TYPE_INVALID,
TYPE_0F38_TABLE = TYPE_INVALID,
TYPE_0F3A_TABLE = TYPE_INVALID,
};
namespace InstFlags {
constexpr uint32_t FLAGS_NONE = 0;
constexpr uint32_t FLAGS_DEBUG = (1 << 1);
constexpr uint32_t FLAGS_DEBUG_MEM_ACCESS = (1 << 2);
constexpr uint32_t FLAGS_SUPPORTS_REP = (1 << 3);
constexpr uint32_t FLAGS_BLOCK_END = (1 << 4);
constexpr uint32_t FLAGS_SETS_RIP = (1 << 5);
constexpr uint32_t FLAGS_DISPLACE_SIZE_MUL_2 = (1 << 6);
constexpr uint32_t FLAGS_DISPLACE_SIZE_DIV_2 = (1 << 7);
constexpr uint32_t FLAGS_SRC_SEXT = (1 << 8);
constexpr uint32_t FLAGS_MEM_OFFSET = (1 << 9);
// Enables XMM based subflags
// Current reserved range for this SF is [10, 15]
constexpr uint32_t FLAGS_XMM_FLAGS = (1 << 10);
// Non-XMM subflags
constexpr uint32_t FLAGS_SF_DST_RAX = (1 << 11);
constexpr uint32_t FLAGS_SF_DST_RDX = (1 << 12);
constexpr uint32_t FLAGS_SF_SRC_RAX = (1 << 13);
constexpr uint32_t FLAGS_SF_SRC_RCX = (1 << 14);
constexpr uint32_t FLAGS_SF_REX_IN_BYTE = (1 << 15);
// XMM subflags
constexpr uint32_t FLAGS_SF_HIGH_XMM_REG = (1 << 11);
constexpr uint32_t FLAGS_SF_DST_GPR = (1 << 12);
constexpr uint32_t FLAGS_SF_SRC_GPR = (1 << 13);
// Enables MODRM specific subflags
// Current reserved range for this SF is [14, 17]
constexpr uint32_t FLAGS_MODRM = (1 << 16);
// With ModRM SF flag enabled
// Direction of ModRM. Dst ^ Src
// Set means destination is rm bits
// Unset means src is rm bits
constexpr uint32_t FLAGS_SF_MOD_DST = (1 << 17);
// If the instruction is restricted to mem or reg only
// 0b00 = Regular ModRM support
// 0b01 = Memory accesses only
// 0b10 = Register accesses only
// 0b11 = <Reserved>
constexpr uint32_t FLAGS_SF_MOD_MEM_ONLY = (1 << 18);
constexpr uint32_t FLAGS_SF_MOD_REG_ONLY = (1 << 19);
constexpr uint32_t FLAGS_SIZE_DST_OFF = 20;
constexpr uint32_t FLAGS_SIZE_SRC_OFF = FLAGS_SIZE_DST_OFF + 3;
constexpr uint32_t SIZE_MASK = 0b111;
constexpr uint32_t SIZE_DEF = 0b000;
constexpr uint32_t SIZE_8BIT = 0b001;
constexpr uint32_t SIZE_16BIT = 0b010;
constexpr uint32_t SIZE_32BIT = 0b011;
constexpr uint32_t SIZE_64BIT = 0b100;
constexpr uint32_t SIZE_128BIT = 0b101;
constexpr uint32_t SIZE_256BIT = 0b110;
inline uint32_t GetSizeDstFlags(uint32_t Flags) { return (Flags >> FLAGS_SIZE_DST_OFF) & SIZE_MASK; }
inline uint32_t GetSizeSrcFlags(uint32_t Flags) { return (Flags >> FLAGS_SIZE_SRC_OFF) & SIZE_MASK; }
inline uint32_t GenFlagsDstSize(uint32_t Size) { return Size << FLAGS_SIZE_DST_OFF; }
inline uint32_t GenFlagsSrcSize(uint32_t Size) { return Size << FLAGS_SIZE_SRC_OFF; }
inline uint32_t GenFlagsSameSize(uint32_t Size) {return (Size << FLAGS_SIZE_DST_OFF) | (Size << FLAGS_SIZE_SRC_OFF); }
inline uint32_t GenFlagsSizes(uint32_t Dest, uint32_t Src) {return (Dest << FLAGS_SIZE_DST_OFF) | (Src << FLAGS_SIZE_SRC_OFF); }
// If it has an xmm subflag
#define HAS_XMM_SUBFLAG(x, flag) (((x) & (FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS | (flag))) == (FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS | (flag)))
// If it has non-xmm subflag
#define HAS_NON_XMM_SUBFLAG(x, flag) (((x) & (FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS | (flag))) == (flag))
}
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;
};
using DecodedOp = DecodedInst const*;
using OpDispatchPtr = void (IR::OpDispatchBuilder::*)(DecodedOp);
#ifndef NDEBUG
namespace X86InstDebugInfo {
constexpr uint64_t FLAGS_MEM_ALIGN_4 = (1 << 0);
constexpr uint64_t FLAGS_MEM_ALIGN_8 = (1 << 1);
constexpr uint64_t FLAGS_MEM_ALIGN_16 = (1 << 2);
constexpr uint64_t FLAGS_MEM_ALIGN_SIZE = (1 << 3); // If instruction size changes depending on prefixes
constexpr uint64_t FLAGS_MEM_ACCESS = (1 << 4);
constexpr uint64_t FLAGS_DEBUG = (1 << 5);
constexpr uint64_t FLAGS_DIVIDE = (1 << 6);
struct Flags {
uint64_t DebugFlags;
};
void InstallDebugInfo();
}
#endif
struct X86InstInfo {
char const *Name;
InstType Type;
uint32_t Flags; ///< Must be larger than InstFlags enum
uint8_t MoreBytes;
OpDispatchPtr OpcodeDispatcher;
#ifndef NDEBUG
X86InstDebugInfo::Flags DebugInfo;
uint32_t NumUnitTestsGenerated;
#endif
bool operator==(const X86InstInfo &b) const {
if (strcmp(Name, b.Name) != 0 ||
Type != b.Type ||
Flags != b.Flags ||
MoreBytes != b.MoreBytes)
return false;
// We don't care if the opcode dispatcher differs
return true;
}
};
static_assert(std::is_pod<X86InstInfo>::value, "Pod?");
constexpr size_t MAX_PRIMARY_TABLE_SIZE = 256;
constexpr size_t MAX_SECOND_TABLE_SIZE = 256;
constexpr size_t MAX_REP_MOD_TABLE_SIZE = 256;
constexpr size_t MAX_REPNE_MOD_TABLE_SIZE = 256;
constexpr size_t MAX_OPSIZE_MOD_TABLE_SIZE = 256;
// 6 (groups) | 6 (max indexes) | 8 ops = 0b111'111'111 = 9 bits
constexpr size_t MAX_INST_GROUP_TABLE_SIZE = 512;
// 12 (groups) | 3(max indexes) | 8 ops = 0b1111'11'111 = 9 bits
constexpr size_t MAX_INST_SECOND_GROUP_TABLE_SIZE = 512;
constexpr size_t MAX_X87_TABLE_SIZE = 1 << 11;
constexpr size_t MAX_SECOND_MODRM_TABLE_SIZE = 32;
// 3 prefixes | 8 bit opcode
constexpr size_t MAX_0F_38_TABLE_SIZE = (1 << 11);
// 1 REX | 1 prefixes | 8 bit opcode
constexpr size_t MAX_0F_3A_TABLE_SIZE = (1 << 11);
constexpr size_t MAX_3DNOW_TABLE_SIZE = 256;
// VEX
// map_select(2 bits for now) | vex.pp (2 bits) | opcode (8bit)
constexpr size_t MAX_VEX_TABLE_SIZE = (1 << 13);
// VEX group ops
// group select (3 bits for now) | ModRM opcode (3 bits)
constexpr size_t MAX_VEX_GROUP_TABLE_SIZE = (1 << 7);
// XOP
// group (2 bits for now) | vex.pp (2 bits) | opcode (8bit)
constexpr size_t MAX_XOP_TABLE_SIZE = (1 << 13);
// XOP group ops
// group select (2 bits for now) | modrm opcode (3 bits)
constexpr size_t MAX_XOP_GROUP_TABLE_SIZE = (1 << 6);
constexpr size_t MAX_EVEX_TABLE_SIZE = 256;
extern X86InstInfo BaseOps[MAX_PRIMARY_TABLE_SIZE];
extern X86InstInfo SecondBaseOps[MAX_SECOND_TABLE_SIZE];
extern X86InstInfo RepModOps[MAX_REP_MOD_TABLE_SIZE];
extern X86InstInfo RepNEModOps[MAX_REPNE_MOD_TABLE_SIZE];
extern X86InstInfo OpSizeModOps[MAX_OPSIZE_MOD_TABLE_SIZE];
extern X86InstInfo PrimaryInstGroupOps[MAX_INST_GROUP_TABLE_SIZE];
extern X86InstInfo SecondInstGroupOps[MAX_INST_SECOND_GROUP_TABLE_SIZE];
extern X86InstInfo SecondModRMTableOps[MAX_SECOND_MODRM_TABLE_SIZE];
extern X86InstInfo X87Ops[MAX_X87_TABLE_SIZE];
extern X86InstInfo DDDNowOps[MAX_3DNOW_TABLE_SIZE];
extern X86InstInfo H0F38TableOps[MAX_0F_38_TABLE_SIZE];
extern X86InstInfo H0F3ATableOps[MAX_0F_3A_TABLE_SIZE];
// VEX
extern X86InstInfo VEXTableOps[MAX_VEX_TABLE_SIZE];
extern X86InstInfo VEXTableGroupOps[MAX_VEX_GROUP_TABLE_SIZE];
// XOP
extern X86InstInfo XOPTableOps[MAX_XOP_TABLE_SIZE];
extern X86InstInfo XOPTableGroupOps[MAX_XOP_GROUP_TABLE_SIZE];
// EVEX
extern X86InstInfo EVEXTableOps[MAX_EVEX_TABLE_SIZE];
void InitializeInfoTables();
}