diff --git a/libxenia.vcxproj b/libxenia.vcxproj
index 53538d182..10f0e4e8c 100644
--- a/libxenia.vcxproj
+++ b/libxenia.vcxproj
@@ -499,7 +499,6 @@
-
diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters
index 8a342f6c9..403883ef8 100644
--- a/libxenia.vcxproj.filters
+++ b/libxenia.vcxproj.filters
@@ -1475,9 +1475,6 @@
-
- src\xenia\cpu\backend\x64
-
src\xenia\cpu\hir
diff --git a/src/xenia/cpu/backend/x64/x64_sequence.inl b/src/xenia/cpu/backend/x64/x64_sequence.inl
deleted file mode 100644
index f89b2f0d1..000000000
--- a/src/xenia/cpu/backend/x64/x64_sequence.inl
+++ /dev/null
@@ -1,784 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-namespace {
-
-enum KeyType {
- KEY_TYPE_X = OPCODE_SIG_TYPE_X,
- KEY_TYPE_L = OPCODE_SIG_TYPE_L,
- KEY_TYPE_O = OPCODE_SIG_TYPE_O,
- KEY_TYPE_S = OPCODE_SIG_TYPE_S,
- KEY_TYPE_V_I8 = OPCODE_SIG_TYPE_V + INT8_TYPE,
- KEY_TYPE_V_I16 = OPCODE_SIG_TYPE_V + INT16_TYPE,
- KEY_TYPE_V_I32 = OPCODE_SIG_TYPE_V + INT32_TYPE,
- KEY_TYPE_V_I64 = OPCODE_SIG_TYPE_V + INT64_TYPE,
- KEY_TYPE_V_F32 = OPCODE_SIG_TYPE_V + FLOAT32_TYPE,
- KEY_TYPE_V_F64 = OPCODE_SIG_TYPE_V + FLOAT64_TYPE,
- KEY_TYPE_V_V128 = OPCODE_SIG_TYPE_V + VEC128_TYPE,
-};
-
-#pragma pack(push, 1)
-union InstrKey {
- struct {
- uint32_t opcode : 8;
- uint32_t dest : 5;
- uint32_t src1 : 5;
- uint32_t src2 : 5;
- uint32_t src3 : 5;
- uint32_t reserved : 4;
- };
- uint32_t value;
-
- operator uint32_t() const { return value; }
-
- InstrKey() : value(0) {}
- InstrKey(uint32_t v) : value(v) {}
- InstrKey(const Instr* i) : value(0) {
- opcode = i->opcode->num;
- uint32_t sig = i->opcode->signature;
- dest =
- GET_OPCODE_SIG_TYPE_DEST(sig) ? OPCODE_SIG_TYPE_V + i->dest->type : 0;
- src1 = GET_OPCODE_SIG_TYPE_SRC1(sig);
- if (src1 == OPCODE_SIG_TYPE_V) {
- src1 += i->src1.value->type;
- }
- src2 = GET_OPCODE_SIG_TYPE_SRC2(sig);
- if (src2 == OPCODE_SIG_TYPE_V) {
- src2 += i->src2.value->type;
- }
- src3 = GET_OPCODE_SIG_TYPE_SRC3(sig);
- if (src3 == OPCODE_SIG_TYPE_V) {
- src3 += i->src3.value->type;
- }
- }
-
- template
- struct Construct {
- static const uint32_t value =
- (OPCODE) | (DEST << 8) | (SRC1 << 13) | (SRC2 << 18) | (SRC3 << 23);
- };
-};
-#pragma pack(pop)
-static_assert(sizeof(InstrKey) <= 4, "Key must be 4 bytes");
-
-template
-struct CombinedStruct;
-template <>
-struct CombinedStruct<> {};
-template
-struct CombinedStruct : T, CombinedStruct {};
-
-struct OpBase {};
-
-template
-struct Op : OpBase {
- static const KeyType key_type = KEY_TYPE;
-};
-
-struct VoidOp : Op {
- protected:
- template
- friend struct Op;
- template
- friend struct I;
- void Load(const Instr::Op& op) {}
-};
-
-struct OffsetOp : Op {
- uint64_t value;
-
- protected:
- template
- friend struct Op;
- template
- friend struct I;
- void Load(const Instr::Op& op) { this->value = op.offset; }
-};
-
-struct SymbolOp : Op {
- FunctionInfo* value;
-
- protected:
- template
- friend struct Op;
- template
- friend struct I;
- bool Load(const Instr::Op& op) {
- this->value = op.symbol_info;
- return true;
- }
-};
-
-struct LabelOp : Op {
- hir::Label* value;
-
- protected:
- template
- friend struct Op;
- template
- friend struct I;
- void Load(const Instr::Op& op) { this->value = op.label; }
-};
-
-template
-struct ValueOp : Op, KEY_TYPE> {
- typedef REG_TYPE reg_type;
- static const int tag = TAG;
- const Value* value;
- bool is_constant;
- virtual bool ConstantFitsIn32Reg() const { return true; }
- const REG_TYPE& reg() const {
- assert_true(!is_constant);
- return reg_;
- }
- operator const REG_TYPE&() const { return reg(); }
- bool IsEqual(const T& b) const {
- if (is_constant && b.is_constant) {
- return reinterpret_cast(this)->constant() == b.constant();
- } else if (!is_constant && !b.is_constant) {
- return reg_.getIdx() == b.reg_.getIdx();
- } else {
- return false;
- }
- }
- bool IsEqual(const Xbyak::Reg& b) const {
- if (is_constant) {
- return false;
- } else if (!is_constant) {
- return reg_.getIdx() == b.getIdx();
- } else {
- return false;
- }
- }
- bool operator==(const T& b) const { return IsEqual(b); }
- bool operator!=(const T& b) const { return !IsEqual(b); }
- bool operator==(const Xbyak::Reg& b) const { return IsEqual(b); }
- bool operator!=(const Xbyak::Reg& b) const { return !IsEqual(b); }
- void Load(const Instr::Op& op) {
- const Value* value = op.value;
- this->value = value;
- is_constant = value->IsConstant();
- if (!is_constant) {
- X64Emitter::SetupReg(value, reg_);
- }
- }
-
- protected:
- REG_TYPE reg_;
-};
-
-template
-struct I8 : ValueOp, KEY_TYPE_V_I8, Reg8, int8_t, TAG> {
- typedef ValueOp, KEY_TYPE_V_I8, Reg8, int8_t, TAG> BASE;
- const int8_t constant() const {
- assert_true(BASE::is_constant);
- return BASE::value->constant.i8;
- }
-};
-template
-struct I16 : ValueOp, KEY_TYPE_V_I16, Reg16, int16_t, TAG> {
- typedef ValueOp, KEY_TYPE_V_I16, Reg16, int16_t, TAG> BASE;
- const int16_t constant() const {
- assert_true(BASE::is_constant);
- return BASE::value->constant.i16;
- }
-};
-template
-struct I32 : ValueOp, KEY_TYPE_V_I32, Reg32, int32_t, TAG> {
- typedef ValueOp, KEY_TYPE_V_I32, Reg32, int32_t, TAG> BASE;
- const int32_t constant() const {
- assert_true(BASE::is_constant);
- return BASE::value->constant.i32;
- }
-};
-template
-struct I64 : ValueOp, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
- typedef ValueOp, KEY_TYPE_V_I64, Reg64, int64_t, TAG> BASE;
- const int64_t constant() const {
- assert_true(BASE::is_constant);
- return BASE::value->constant.i64;
- }
- bool ConstantFitsIn32Reg() const override {
- int64_t v = BASE::value->constant.i64;
- if ((v & ~0x7FFFFFFF) == 0) {
- // Fits under 31 bits, so just load using normal mov.
- return true;
- } else if ((v & ~0x7FFFFFFF) == ~0x7FFFFFFF) {
- // Negative number that fits in 32bits.
- return true;
- }
- return false;
- }
-};
-template
-struct F32 : ValueOp, KEY_TYPE_V_F32, Xmm, float, TAG> {
- typedef ValueOp, KEY_TYPE_V_F32, Xmm, float, TAG> BASE;
- const float constant() const {
- assert_true(BASE::is_constant);
- return BASE::value->constant.f32;
- }
-};
-template
-struct F64 : ValueOp, KEY_TYPE_V_F64, Xmm, double, TAG> {
- typedef ValueOp, KEY_TYPE_V_F64, Xmm, double, TAG> BASE;
- const double constant() const {
- assert_true(BASE::is_constant);
- return BASE::value->constant.f64;
- }
-};
-template
-struct V128 : ValueOp, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> {
- typedef ValueOp, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> BASE;
- const vec128_t& constant() const {
- assert_true(BASE::is_constant);
- return BASE::value->constant.v128;
- }
-};
-
-struct TagTable {
- struct {
- bool valid;
- Instr::Op op;
- } table[16];
-
- template ::type* = nullptr>
- bool CheckTag(const Instr::Op& op) {
- return true;
- }
- template ::type* = nullptr>
- bool CheckTag(const Instr::Op& op) {
- return true;
- }
- template ::type* = nullptr>
- bool CheckTag(const Instr::Op& op) {
- return true;
- }
- template ::type* = nullptr>
- bool CheckTag(const Instr::Op& op) {
- return true;
- }
- template =
- KEY_TYPE_V_I8>::type* = nullptr>
- bool CheckTag(const Instr::Op& op) {
- const Value* value = op.value;
- if (T::tag == -1) {
- return true;
- }
- if (table[T::tag].valid && table[T::tag].op.value != value) {
- return false;
- }
- table[T::tag].valid = true;
- table[T::tag].op.value = (Value*)value;
- return true;
- }
-};
-
-template
-struct DestField;
-template
-struct DestField {
- DEST dest;
-
- protected:
- bool LoadDest(const Instr* i, TagTable& tag_table) {
- Instr::Op op;
- op.value = i->dest;
- if (tag_table.CheckTag(op)) {
- dest.Load(op);
- return true;
- }
- return false;
- }
-};
-template <>
-struct DestField {
- protected:
- bool LoadDest(const Instr* i, TagTable& tag_table) { return true; }
-};
-
-template
-struct I;
-template
-struct I : DestField {
- typedef DestField BASE;
- static const hir::Opcode opcode = OPCODE;
- static const uint32_t key =
- InstrKey::Construct::value;
- static const KeyType dest_type = DEST::key_type;
- const Instr* instr;
-
- protected:
- template
- friend struct SequenceFields;
- bool Load(const Instr* i, TagTable& tag_table) {
- if (InstrKey(i).value == key && BASE::LoadDest(i, tag_table)) {
- instr = i;
- return true;
- }
- return false;
- }
-};
-template
-struct I : DestField {
- typedef DestField BASE;
- static const hir::Opcode opcode = OPCODE;
- static const uint32_t key =
- InstrKey::Construct::value;
- static const KeyType dest_type = DEST::key_type;
- static const KeyType src1_type = SRC1::key_type;
- const Instr* instr;
- SRC1 src1;
-
- protected:
- template
- friend struct SequenceFields;
- bool Load(const Instr* i, TagTable& tag_table) {
- if (InstrKey(i).value == key && BASE::LoadDest(i, tag_table) &&
- tag_table.CheckTag(i->src1)) {
- instr = i;
- src1.Load(i->src1);
- return true;
- }
- return false;
- }
-};
-template
-struct I : DestField {
- typedef DestField BASE;
- static const hir::Opcode opcode = OPCODE;
- static const uint32_t key =
- InstrKey::Construct::value;
- static const KeyType dest_type = DEST::key_type;
- static const KeyType src1_type = SRC1::key_type;
- static const KeyType src2_type = SRC2::key_type;
- const Instr* instr;
- SRC1 src1;
- SRC2 src2;
-
- protected:
- template
- friend struct SequenceFields;
- bool Load(const Instr* i, TagTable& tag_table) {
- if (InstrKey(i).value == key && BASE::LoadDest(i, tag_table) &&
- tag_table.CheckTag(i->src1) &&
- tag_table.CheckTag(i->src2)) {
- instr = i;
- src1.Load(i->src1);
- src2.Load(i->src2);
- return true;
- }
- return false;
- }
-};
-template
-struct I : DestField {
- typedef DestField BASE;
- static const hir::Opcode opcode = OPCODE;
- static const uint32_t key =
- InstrKey::Construct::value;
- static const KeyType dest_type = DEST::key_type;
- static const KeyType src1_type = SRC1::key_type;
- static const KeyType src2_type = SRC2::key_type;
- static const KeyType src3_type = SRC3::key_type;
- const Instr* instr;
- SRC1 src1;
- SRC2 src2;
- SRC3 src3;
-
- protected:
- template
- friend struct SequenceFields;
- bool Load(const Instr* i, TagTable& tag_table) {
- if (InstrKey(i).value == key && BASE::LoadDest(i, tag_table) &&
- tag_table.CheckTag(i->src1) &&
- tag_table.CheckTag(i->src2) &&
- tag_table.CheckTag(i->src3)) {
- instr = i;
- src1.Load(i->src1);
- src2.Load(i->src2);
- src3.Load(i->src3);
- return true;
- }
- return false;
- }
-};
-
-template
-struct SequenceFields;
-template
-struct SequenceFields {
- I1 i1;
-
- protected:
- template
- friend struct Sequence;
- bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
- if (i1.Load(i, tag_table)) {
- *new_tail = i->next;
- return true;
- }
- return false;
- }
-};
-template
-struct SequenceFields : SequenceFields {
- I2 i2;
-
- protected:
- template
- friend struct Sequence;
- bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
- if (SequenceFields::Check(i, tag_table, new_tail)) {
- auto ni = i->next;
- if (ni && i2.Load(ni, tag_table)) {
- *new_tail = ni;
- return i;
- }
- }
- return false;
- }
-};
-template
-struct SequenceFields : SequenceFields {
- I3 i3;
-
- protected:
- template
- friend struct Sequence;
- bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
- if (SequenceFields::Check(i, tag_table, new_tail)) {
- auto ni = i->next;
- if (ni && i3.Load(ni, tag_table)) {
- *new_tail = ni;
- return i;
- }
- }
- return false;
- }
-};
-template
-struct SequenceFields : SequenceFields {
- I4 i4;
-
- protected:
- template
- friend struct Sequence;
- bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
- if (SequenceFields::Check(i, tag_table, new_tail)) {
- auto ni = i->next;
- if (ni && i4.Load(ni, tag_table)) {
- *new_tail = ni;
- return i;
- }
- }
- return false;
- }
-};
-template
-struct SequenceFields : SequenceFields {
- I5 i5;
-
- protected:
- template
- friend struct Sequence;
- bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
- if (SequenceFields::Check(i, tag_table, new_tail)) {
- auto ni = i->next;
- if (ni && i5.Load(ni, tag_table)) {
- *new_tail = ni;
- return i;
- }
- }
- return false;
- }
-};
-
-template
-struct Sequence {
- struct EmitArgs : SequenceFields {};
-
- static bool Select(X64Emitter& e, const Instr* i, const Instr** new_tail) {
- EmitArgs args;
- TagTable tag_table;
- if (!args.Check(i, tag_table, new_tail)) {
- return false;
- }
- SEQ::Emit(e, args);
- return true;
- }
-};
-
-template
-const T GetTempReg(X64Emitter& e);
-template <>
-const Reg8 GetTempReg(X64Emitter& e) {
- return e.al;
-}
-template <>
-const Reg16 GetTempReg(X64Emitter& e) {
- return e.ax;
-}
-template <>
-const Reg32 GetTempReg(X64Emitter& e) {
- return e.eax;
-}
-template <>
-const Reg64 GetTempReg(X64Emitter& e) {
- return e.rax;
-}
-
-template
-struct SingleSequence : public Sequence, T> {
- typedef Sequence, T> BASE;
- typedef T EmitArgType;
- static constexpr uint32_t head_key() { return T::key; }
- static void Emit(X64Emitter& e, const typename BASE::EmitArgs& _) {
- SEQ::Emit(e, _.i1);
- }
-
- template
- static void EmitUnaryOp(X64Emitter& e, const EmitArgType& i,
- const REG_FN& reg_fn) {
- if (i.src1.is_constant) {
- e.mov(i.dest, i.src1.constant());
- reg_fn(e, i.dest);
- } else {
- if (i.dest != i.src1) {
- e.mov(i.dest, i.src1);
- }
- reg_fn(e, i.dest);
- }
- }
-
- template
- static void EmitCommutativeBinaryOp(X64Emitter& e, const EmitArgType& i,
- const REG_REG_FN& reg_reg_fn,
- const REG_CONST_FN& reg_const_fn) {
- if (i.src1.is_constant) {
- if (i.src2.is_constant) {
- if (i.src1.ConstantFitsIn32Reg()) {
- e.mov(i.dest, i.src2.constant());
- reg_const_fn(e, i.dest, static_cast(i.src1.constant()));
- } else if (i.src2.ConstantFitsIn32Reg()) {
- e.mov(i.dest, i.src1.constant());
- reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
- } else {
- e.mov(i.dest, i.src1.constant());
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2.constant());
- reg_reg_fn(e, i.dest, temp);
- }
- } else {
- if (i.dest == i.src2) {
- if (i.src1.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.dest, static_cast(i.src1.constant()));
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src1.constant());
- reg_reg_fn(e, i.dest, temp);
- }
- } else {
- e.mov(i.dest, i.src1.constant());
- reg_reg_fn(e, i.dest, i.src2);
- }
- }
- } else if (i.src2.is_constant) {
- if (i.dest == i.src1) {
- if (i.src2.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2.constant());
- reg_reg_fn(e, i.dest, temp);
- }
- } else {
- e.mov(i.dest, i.src2.constant());
- reg_reg_fn(e, i.dest, i.src1);
- }
- } else {
- if (i.dest == i.src1) {
- reg_reg_fn(e, i.dest, i.src2);
- } else if (i.dest == i.src2) {
- reg_reg_fn(e, i.dest, i.src1);
- } else {
- e.mov(i.dest, i.src1);
- reg_reg_fn(e, i.dest, i.src2);
- }
- }
- }
- template
- static void EmitAssociativeBinaryOp(X64Emitter& e, const EmitArgType& i,
- const REG_REG_FN& reg_reg_fn,
- const REG_CONST_FN& reg_const_fn) {
- if (i.src1.is_constant) {
- assert_true(!i.src2.is_constant);
- if (i.dest == i.src2) {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2);
- e.mov(i.dest, i.src1.constant());
- reg_reg_fn(e, i.dest, temp);
- } else {
- e.mov(i.dest, i.src1.constant());
- reg_reg_fn(e, i.dest, i.src2);
- }
- } else if (i.src2.is_constant) {
- if (i.dest == i.src1) {
- if (i.src2.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2.constant());
- reg_reg_fn(e, i.dest, temp);
- }
- } else {
- e.mov(i.dest, i.src1);
- if (i.src2.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2.constant());
- reg_reg_fn(e, i.dest, temp);
- }
- }
- } else {
- if (i.dest == i.src1) {
- reg_reg_fn(e, i.dest, i.src2);
- } else if (i.dest == i.src2) {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2);
- e.mov(i.dest, i.src1);
- reg_reg_fn(e, i.dest, temp);
- } else {
- e.mov(i.dest, i.src1);
- reg_reg_fn(e, i.dest, i.src2);
- }
- }
- }
-
- template
- static void EmitCommutativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
- const FN& fn) {
- if (i.src1.is_constant) {
- assert_true(!i.src2.is_constant);
- e.LoadConstantXmm(e.xmm0, i.src1.constant());
- fn(e, i.dest, e.xmm0, i.src2);
- } else if (i.src2.is_constant) {
- e.LoadConstantXmm(e.xmm0, i.src2.constant());
- fn(e, i.dest, i.src1, e.xmm0);
- } else {
- fn(e, i.dest, i.src1, i.src2);
- }
- }
-
- template
- static void EmitAssociativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
- const FN& fn) {
- if (i.src1.is_constant) {
- assert_true(!i.src2.is_constant);
- e.LoadConstantXmm(e.xmm0, i.src1.constant());
- fn(e, i.dest, e.xmm0, i.src2);
- } else if (i.src2.is_constant) {
- e.LoadConstantXmm(e.xmm0, i.src2.constant());
- fn(e, i.dest, i.src1, e.xmm0);
- } else {
- fn(e, i.dest, i.src1, i.src2);
- }
- }
-
- template
- static void EmitCommutativeCompareOp(X64Emitter& e, const EmitArgType& i,
- const REG_REG_FN& reg_reg_fn,
- const REG_CONST_FN& reg_const_fn) {
- if (i.src1.is_constant) {
- assert_true(!i.src2.is_constant);
- if (i.src1.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.src2, static_cast(i.src1.constant()));
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src1.constant());
- reg_reg_fn(e, i.src2, temp);
- }
- } else if (i.src2.is_constant) {
- if (i.src2.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.src1, static_cast(i.src2.constant()));
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2.constant());
- reg_reg_fn(e, i.src1, temp);
- }
- } else {
- reg_reg_fn(e, i.src1, i.src2);
- }
- }
- template
- static void EmitAssociativeCompareOp(X64Emitter& e, const EmitArgType& i,
- const REG_REG_FN& reg_reg_fn,
- const REG_CONST_FN& reg_const_fn) {
- if (i.src1.is_constant) {
- assert_true(!i.src2.is_constant);
- if (i.src1.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.dest, i.src2, static_cast(i.src1.constant()),
- true);
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src1.constant());
- reg_reg_fn(e, i.dest, i.src2, temp, true);
- }
- } else if (i.src2.is_constant) {
- if (i.src2.ConstantFitsIn32Reg()) {
- reg_const_fn(e, i.dest, i.src1, static_cast(i.src2.constant()),
- false);
- } else {
- auto temp = GetTempReg(e);
- e.mov(temp, i.src2.constant());
- reg_reg_fn(e, i.dest, i.src1, temp, false);
- }
- } else {
- reg_reg_fn(e, i.dest, i.src1, i.src2, false);
- }
- }
-};
-
-static const int ANY = -1;
-typedef int tag_t;
-static const tag_t TAG0 = 0;
-static const tag_t TAG1 = 1;
-static const tag_t TAG2 = 2;
-static const tag_t TAG3 = 3;
-static const tag_t TAG4 = 4;
-static const tag_t TAG5 = 5;
-static const tag_t TAG6 = 6;
-static const tag_t TAG7 = 7;
-
-template
-void Register() {
- sequence_table.insert({T::head_key(), T::Select});
-}
-template
-void Register() {
- Register();
- Register();
-};
-#define EMITTER_OPCODE_TABLE(name, ...) \
- void Register_##name() { Register<__VA_ARGS__>(); }
-
-#define MATCH(...) __VA_ARGS__
-#define EMITTER(name, match) struct name : SingleSequence
-#define SEQUENCE(name, match) struct name : Sequence
-
-} // namespace
diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc
index 575d89a81..c848f16a5 100644
--- a/src/xenia/cpu/backend/x64/x64_sequences.cc
+++ b/src/xenia/cpu/backend/x64/x64_sequences.cc
@@ -28,6 +28,7 @@
#include "xenia/cpu/backend/x64/x64_sequences.h"
#include
+#include
#include "xenia/base/assert.h"
#include "xenia/base/clock.h"
@@ -52,11 +53,8 @@ using namespace Xbyak;
using namespace xe::cpu::hir;
using namespace xe::cpu;
-typedef bool (*SequenceSelectFn)(X64Emitter&, const Instr*, const Instr**);
-std::unordered_multimap sequence_table;
-
-// Utilities/types used only in this file:
-#include "xenia/cpu/backend/x64/x64_sequence.inl"
+typedef bool (*SequenceSelectFn)(X64Emitter&, const Instr*);
+std::unordered_map sequence_table;
// Selects the right byte/word/etc from a vector. We need to flip logical
// indices (0,1,2,3,4,5,6,7,...) = (3,2,1,0,7,6,5,4,...)
@@ -65,6 +63,606 @@ std::unordered_multimap sequence_table;
#define VEC128_D(n) (n)
#define VEC128_F(n) (n)
+enum KeyType {
+ KEY_TYPE_X = OPCODE_SIG_TYPE_X,
+ KEY_TYPE_L = OPCODE_SIG_TYPE_L,
+ KEY_TYPE_O = OPCODE_SIG_TYPE_O,
+ KEY_TYPE_S = OPCODE_SIG_TYPE_S,
+ KEY_TYPE_V_I8 = OPCODE_SIG_TYPE_V + INT8_TYPE,
+ KEY_TYPE_V_I16 = OPCODE_SIG_TYPE_V + INT16_TYPE,
+ KEY_TYPE_V_I32 = OPCODE_SIG_TYPE_V + INT32_TYPE,
+ KEY_TYPE_V_I64 = OPCODE_SIG_TYPE_V + INT64_TYPE,
+ KEY_TYPE_V_F32 = OPCODE_SIG_TYPE_V + FLOAT32_TYPE,
+ KEY_TYPE_V_F64 = OPCODE_SIG_TYPE_V + FLOAT64_TYPE,
+ KEY_TYPE_V_V128 = OPCODE_SIG_TYPE_V + VEC128_TYPE,
+};
+
+#pragma pack(push, 1)
+union InstrKey {
+ struct {
+ uint32_t opcode : 8;
+ uint32_t dest : 5;
+ uint32_t src1 : 5;
+ uint32_t src2 : 5;
+ uint32_t src3 : 5;
+ uint32_t reserved : 4;
+ };
+ uint32_t value;
+
+ operator uint32_t() const { return value; }
+
+ InstrKey() : value(0) {}
+ InstrKey(uint32_t v) : value(v) {}
+ InstrKey(const Instr* i) : value(0) {
+ opcode = i->opcode->num;
+ uint32_t sig = i->opcode->signature;
+ dest =
+ GET_OPCODE_SIG_TYPE_DEST(sig) ? OPCODE_SIG_TYPE_V + i->dest->type : 0;
+ src1 = GET_OPCODE_SIG_TYPE_SRC1(sig);
+ if (src1 == OPCODE_SIG_TYPE_V) {
+ src1 += i->src1.value->type;
+ }
+ src2 = GET_OPCODE_SIG_TYPE_SRC2(sig);
+ if (src2 == OPCODE_SIG_TYPE_V) {
+ src2 += i->src2.value->type;
+ }
+ src3 = GET_OPCODE_SIG_TYPE_SRC3(sig);
+ if (src3 == OPCODE_SIG_TYPE_V) {
+ src3 += i->src3.value->type;
+ }
+ }
+
+ template
+ struct Construct {
+ static const uint32_t value =
+ (OPCODE) | (DEST << 8) | (SRC1 << 13) | (SRC2 << 18) | (SRC3 << 23);
+ };
+};
+#pragma pack(pop)
+static_assert(sizeof(InstrKey) <= 4, "Key must be 4 bytes");
+
+template
+struct CombinedStruct;
+template <>
+struct CombinedStruct<> {};
+template
+struct CombinedStruct : T, CombinedStruct {};
+
+struct OpBase {};
+
+template
+struct Op : OpBase {
+ static const KeyType key_type = KEY_TYPE;
+};
+
+struct VoidOp : Op {
+ protected:
+ template
+ friend struct Op;
+ template
+ friend struct I;
+ void Load(const Instr::Op& op) {}
+};
+
+struct OffsetOp : Op {
+ uint64_t value;
+
+ protected:
+ template
+ friend struct Op;
+ template
+ friend struct I;
+ void Load(const Instr::Op& op) { this->value = op.offset; }
+};
+
+struct SymbolOp : Op {
+ FunctionInfo* value;
+
+ protected:
+ template
+ friend struct Op;
+ template
+ friend struct I;
+ bool Load(const Instr::Op& op) {
+ this->value = op.symbol_info;
+ return true;
+ }
+};
+
+struct LabelOp : Op {
+ hir::Label* value;
+
+ protected:
+ template
+ friend struct Op;
+ template
+ friend struct I;
+ void Load(const Instr::Op& op) { this->value = op.label; }
+};
+
+template
+struct ValueOp : Op, KEY_TYPE> {
+ typedef REG_TYPE reg_type;
+ const Value* value;
+ bool is_constant;
+ virtual bool ConstantFitsIn32Reg() const { return true; }
+ const REG_TYPE& reg() const {
+ assert_true(!is_constant);
+ return reg_;
+ }
+ operator const REG_TYPE&() const { return reg(); }
+ bool IsEqual(const T& b) const {
+ if (is_constant && b.is_constant) {
+ return reinterpret_cast(this)->constant() == b.constant();
+ } else if (!is_constant && !b.is_constant) {
+ return reg_.getIdx() == b.reg_.getIdx();
+ } else {
+ return false;
+ }
+ }
+ bool IsEqual(const Xbyak::Reg& b) const {
+ if (is_constant) {
+ return false;
+ } else if (!is_constant) {
+ return reg_.getIdx() == b.getIdx();
+ } else {
+ return false;
+ }
+ }
+ bool operator==(const T& b) const { return IsEqual(b); }
+ bool operator!=(const T& b) const { return !IsEqual(b); }
+ bool operator==(const Xbyak::Reg& b) const { return IsEqual(b); }
+ bool operator!=(const Xbyak::Reg& b) const { return !IsEqual(b); }
+ void Load(const Instr::Op& op) {
+ const Value* value = op.value;
+ this->value = value;
+ is_constant = value->IsConstant();
+ if (!is_constant) {
+ X64Emitter::SetupReg(value, reg_);
+ }
+ }
+
+ protected:
+ REG_TYPE reg_;
+};
+
+struct I8Op : ValueOp {
+ typedef ValueOp BASE;
+ const int8_t constant() const {
+ assert_true(BASE::is_constant);
+ return BASE::value->constant.i8;
+ }
+};
+struct I16Op : ValueOp {
+ typedef ValueOp BASE;
+ const int16_t constant() const {
+ assert_true(BASE::is_constant);
+ return BASE::value->constant.i16;
+ }
+};
+struct I32Op : ValueOp {
+ typedef ValueOp BASE;
+ const int32_t constant() const {
+ assert_true(BASE::is_constant);
+ return BASE::value->constant.i32;
+ }
+};
+struct I64Op : ValueOp {
+ typedef ValueOp BASE;
+ const int64_t constant() const {
+ assert_true(BASE::is_constant);
+ return BASE::value->constant.i64;
+ }
+ bool ConstantFitsIn32Reg() const override {
+ int64_t v = BASE::value->constant.i64;
+ if ((v & ~0x7FFFFFFF) == 0) {
+ // Fits under 31 bits, so just load using normal mov.
+ return true;
+ } else if ((v & ~0x7FFFFFFF) == ~0x7FFFFFFF) {
+ // Negative number that fits in 32bits.
+ return true;
+ }
+ return false;
+ }
+};
+struct F32Op : ValueOp {
+ typedef ValueOp BASE;
+ const float constant() const {
+ assert_true(BASE::is_constant);
+ return BASE::value->constant.f32;
+ }
+};
+struct F64Op : ValueOp {
+ typedef ValueOp BASE;
+ const double constant() const {
+ assert_true(BASE::is_constant);
+ return BASE::value->constant.f64;
+ }
+};
+struct V128Op : ValueOp {
+ typedef ValueOp BASE;
+ const vec128_t& constant() const {
+ assert_true(BASE::is_constant);
+ return BASE::value->constant.v128;
+ }
+};
+
+template
+struct DestField;
+template
+struct DestField {
+ DEST dest;
+
+ protected:
+ bool LoadDest(const Instr* i) {
+ Instr::Op op;
+ op.value = i->dest;
+ dest.Load(op);
+ return true;
+ }
+};
+template <>
+struct DestField {
+ protected:
+ bool LoadDest(const Instr* i) { return true; }
+};
+
+template
+struct I;
+template
+struct I : DestField {
+ typedef DestField BASE;
+ static const hir::Opcode opcode = OPCODE;
+ static const uint32_t key =
+ InstrKey::Construct::value;
+ static const KeyType dest_type = DEST::key_type;
+ const Instr* instr;
+
+ protected:
+ template
+ friend struct Sequence;
+ bool Load(const Instr* i) {
+ if (InstrKey(i).value == key && BASE::LoadDest(i)) {
+ instr = i;
+ return true;
+ }
+ return false;
+ }
+};
+template
+struct I : DestField {
+ typedef DestField BASE;
+ static const hir::Opcode opcode = OPCODE;
+ static const uint32_t key =
+ InstrKey::Construct::value;
+ static const KeyType dest_type = DEST::key_type;
+ static const KeyType src1_type = SRC1::key_type;
+ const Instr* instr;
+ SRC1 src1;
+
+ protected:
+ template
+ friend struct Sequence;
+ bool Load(const Instr* i) {
+ if (InstrKey(i).value == key && BASE::LoadDest(i)) {
+ instr = i;
+ src1.Load(i->src1);
+ return true;
+ }
+ return false;
+ }
+};
+template
+struct I : DestField {
+ typedef DestField BASE;
+ static const hir::Opcode opcode = OPCODE;
+ static const uint32_t key =
+ InstrKey::Construct::value;
+ static const KeyType dest_type = DEST::key_type;
+ static const KeyType src1_type = SRC1::key_type;
+ static const KeyType src2_type = SRC2::key_type;
+ const Instr* instr;
+ SRC1 src1;
+ SRC2 src2;
+
+ protected:
+ template
+ friend struct Sequence;
+ bool Load(const Instr* i) {
+ if (InstrKey(i).value == key && BASE::LoadDest(i)) {
+ instr = i;
+ src1.Load(i->src1);
+ src2.Load(i->src2);
+ return true;
+ }
+ return false;
+ }
+};
+template
+struct I : DestField {
+ typedef DestField BASE;
+ static const hir::Opcode opcode = OPCODE;
+ static const uint32_t key =
+ InstrKey::Construct::value;
+ static const KeyType dest_type = DEST::key_type;
+ static const KeyType src1_type = SRC1::key_type;
+ static const KeyType src2_type = SRC2::key_type;
+ static const KeyType src3_type = SRC3::key_type;
+ const Instr* instr;
+ SRC1 src1;
+ SRC2 src2;
+ SRC3 src3;
+
+ protected:
+ template
+ friend struct Sequence;
+ bool Load(const Instr* i) {
+ if (InstrKey(i).value == key && BASE::LoadDest(i)) {
+ instr = i;
+ src1.Load(i->src1);
+ src2.Load(i->src2);
+ src3.Load(i->src3);
+ return true;
+ }
+ return false;
+ }
+};
+
+template
+struct Sequence {
+ typedef T EmitArgType;
+
+ static constexpr uint32_t head_key() { return T::key; }
+
+ static bool Select(X64Emitter& e, const Instr* i) {
+ T args;
+ if (!args.Load(i)) {
+ return false;
+ }
+ SEQ::Emit(e, args);
+ return true;
+ }
+
+ template
+ static void EmitUnaryOp(X64Emitter& e, const EmitArgType& i,
+ const REG_FN& reg_fn) {
+ if (i.src1.is_constant) {
+ e.mov(i.dest, i.src1.constant());
+ reg_fn(e, i.dest);
+ } else {
+ if (i.dest != i.src1) {
+ e.mov(i.dest, i.src1);
+ }
+ reg_fn(e, i.dest);
+ }
+ }
+
+ template
+ static void EmitCommutativeBinaryOp(X64Emitter& e, const EmitArgType& i,
+ const REG_REG_FN& reg_reg_fn,
+ const REG_CONST_FN& reg_const_fn) {
+ if (i.src1.is_constant) {
+ if (i.src2.is_constant) {
+ if (i.src1.ConstantFitsIn32Reg()) {
+ e.mov(i.dest, i.src2.constant());
+ reg_const_fn(e, i.dest, static_cast(i.src1.constant()));
+ } else if (i.src2.ConstantFitsIn32Reg()) {
+ e.mov(i.dest, i.src1.constant());
+ reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
+ } else {
+ e.mov(i.dest, i.src1.constant());
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2.constant());
+ reg_reg_fn(e, i.dest, temp);
+ }
+ } else {
+ if (i.dest == i.src2) {
+ if (i.src1.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.dest, static_cast(i.src1.constant()));
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src1.constant());
+ reg_reg_fn(e, i.dest, temp);
+ }
+ } else {
+ e.mov(i.dest, i.src1.constant());
+ reg_reg_fn(e, i.dest, i.src2);
+ }
+ }
+ } else if (i.src2.is_constant) {
+ if (i.dest == i.src1) {
+ if (i.src2.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2.constant());
+ reg_reg_fn(e, i.dest, temp);
+ }
+ } else {
+ e.mov(i.dest, i.src2.constant());
+ reg_reg_fn(e, i.dest, i.src1);
+ }
+ } else {
+ if (i.dest == i.src1) {
+ reg_reg_fn(e, i.dest, i.src2);
+ } else if (i.dest == i.src2) {
+ reg_reg_fn(e, i.dest, i.src1);
+ } else {
+ e.mov(i.dest, i.src1);
+ reg_reg_fn(e, i.dest, i.src2);
+ }
+ }
+ }
+ template
+ static void EmitAssociativeBinaryOp(X64Emitter& e, const EmitArgType& i,
+ const REG_REG_FN& reg_reg_fn,
+ const REG_CONST_FN& reg_const_fn) {
+ if (i.src1.is_constant) {
+ assert_true(!i.src2.is_constant);
+ if (i.dest == i.src2) {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2);
+ e.mov(i.dest, i.src1.constant());
+ reg_reg_fn(e, i.dest, temp);
+ } else {
+ e.mov(i.dest, i.src1.constant());
+ reg_reg_fn(e, i.dest, i.src2);
+ }
+ } else if (i.src2.is_constant) {
+ if (i.dest == i.src1) {
+ if (i.src2.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2.constant());
+ reg_reg_fn(e, i.dest, temp);
+ }
+ } else {
+ e.mov(i.dest, i.src1);
+ if (i.src2.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.dest, static_cast(i.src2.constant()));
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2.constant());
+ reg_reg_fn(e, i.dest, temp);
+ }
+ }
+ } else {
+ if (i.dest == i.src1) {
+ reg_reg_fn(e, i.dest, i.src2);
+ } else if (i.dest == i.src2) {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2);
+ e.mov(i.dest, i.src1);
+ reg_reg_fn(e, i.dest, temp);
+ } else {
+ e.mov(i.dest, i.src1);
+ reg_reg_fn(e, i.dest, i.src2);
+ }
+ }
+ }
+
+ template
+ static void EmitCommutativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
+ const FN& fn) {
+ if (i.src1.is_constant) {
+ assert_true(!i.src2.is_constant);
+ e.LoadConstantXmm(e.xmm0, i.src1.constant());
+ fn(e, i.dest, e.xmm0, i.src2);
+ } else if (i.src2.is_constant) {
+ e.LoadConstantXmm(e.xmm0, i.src2.constant());
+ fn(e, i.dest, i.src1, e.xmm0);
+ } else {
+ fn(e, i.dest, i.src1, i.src2);
+ }
+ }
+
+ template
+ static void EmitAssociativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
+ const FN& fn) {
+ if (i.src1.is_constant) {
+ assert_true(!i.src2.is_constant);
+ e.LoadConstantXmm(e.xmm0, i.src1.constant());
+ fn(e, i.dest, e.xmm0, i.src2);
+ } else if (i.src2.is_constant) {
+ e.LoadConstantXmm(e.xmm0, i.src2.constant());
+ fn(e, i.dest, i.src1, e.xmm0);
+ } else {
+ fn(e, i.dest, i.src1, i.src2);
+ }
+ }
+
+ template
+ static void EmitCommutativeCompareOp(X64Emitter& e, const EmitArgType& i,
+ const REG_REG_FN& reg_reg_fn,
+ const REG_CONST_FN& reg_const_fn) {
+ if (i.src1.is_constant) {
+ assert_true(!i.src2.is_constant);
+ if (i.src1.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.src2, static_cast(i.src1.constant()));
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src1.constant());
+ reg_reg_fn(e, i.src2, temp);
+ }
+ } else if (i.src2.is_constant) {
+ if (i.src2.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.src1, static_cast(i.src2.constant()));
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2.constant());
+ reg_reg_fn(e, i.src1, temp);
+ }
+ } else {
+ reg_reg_fn(e, i.src1, i.src2);
+ }
+ }
+ template
+ static void EmitAssociativeCompareOp(X64Emitter& e, const EmitArgType& i,
+ const REG_REG_FN& reg_reg_fn,
+ const REG_CONST_FN& reg_const_fn) {
+ if (i.src1.is_constant) {
+ assert_true(!i.src2.is_constant);
+ if (i.src1.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.dest, i.src2, static_cast(i.src1.constant()),
+ true);
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src1.constant());
+ reg_reg_fn(e, i.dest, i.src2, temp, true);
+ }
+ } else if (i.src2.is_constant) {
+ if (i.src2.ConstantFitsIn32Reg()) {
+ reg_const_fn(e, i.dest, i.src1, static_cast(i.src2.constant()),
+ false);
+ } else {
+ auto temp = GetTempReg(e);
+ e.mov(temp, i.src2.constant());
+ reg_reg_fn(e, i.dest, i.src1, temp, false);
+ }
+ } else {
+ reg_reg_fn(e, i.dest, i.src1, i.src2, false);
+ }
+ }
+};
+
+template
+const T GetTempReg(X64Emitter& e);
+template <>
+const Reg8 GetTempReg(X64Emitter& e) {
+ return e.al;
+}
+template <>
+const Reg16 GetTempReg(X64Emitter& e) {
+ return e.ax;
+}
+template <>
+const Reg32 GetTempReg(X64Emitter& e) {
+ return e.eax;
+}
+template <>
+const Reg64 GetTempReg