diff --git a/src/xenia/cpu/backend/x64/x64_op.h b/src/xenia/cpu/backend/x64/x64_op.h new file mode 100644 index 000000000..f71338304 --- /dev/null +++ b/src/xenia/cpu/backend/x64/x64_op.h @@ -0,0 +1,629 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2018 Xenia Developers. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ +#ifndef XENIA_CPU_BACKEND_X64_X64_OP_H_ +#define XENIA_CPU_BACKEND_X64_X64_OP_H_ + +#include "xenia/cpu/backend/x64/x64_emitter.h" + +#include "xenia/cpu/hir/instr.h" + +namespace xe { +namespace cpu { +namespace backend { +namespace x64 { + +// TODO(benvanik): direct usings. +using namespace xe::cpu; +using namespace xe::cpu::hir; +using namespace Xbyak; + +// 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,...) +#define VEC128_B(n) ((n) ^ 0x3) +#define VEC128_W(n) ((n) ^ 0x1) +#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 { + Function* value; + + protected: + template + friend struct Op; + template + friend struct I; + bool Load(const Instr::Op& op) { + this->value = op.symbol; + 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) { + value = op.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 +static 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 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) { + // Both constants. + 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 { + // src1 constant. + 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) { + assert_true(!i.src1.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) { + assert_true(!i.src1.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) { + assert_true(!i.src1.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) { + assert_true(!i.src1.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); + } + } +}; + +} // namespace x64 +} // namespace backend +} // namespace cpu +} // namespace xe + +#endif // XENIA_CPU_BACKEND_X64_X64_OP_H_ diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index b31bc1b39..4fea97523 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -33,6 +33,7 @@ #include "xenia/base/logging.h" #include "xenia/base/threading.h" #include "xenia/cpu/backend/x64/x64_emitter.h" +#include "xenia/cpu/backend/x64/x64_op.h" #include "xenia/cpu/backend/x64/x64_tracers.h" #include "xenia/cpu/hir/hir_builder.h" #include "xenia/cpu/processor.h" @@ -56,619 +57,6 @@ using xe::cpu::hir::Instr; 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,...) -#define VEC128_B(n) ((n) ^ 0x3) -#define VEC128_W(n) ((n) ^ 0x1) -#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 { - Function* value; - - protected: - template - friend struct Op; - template - friend struct I; - bool Load(const Instr::Op& op) { - this->value = op.symbol; - 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) { - value = op.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 -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 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) { - // Both constants. - 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 { - // src1 constant. - 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) { - assert_true(!i.src1.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) { - assert_true(!i.src1.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) { - assert_true(!i.src1.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) { - assert_true(!i.src1.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 -static bool Register() { - sequence_table.insert({T::head_key(), T::Select}); - return true; -} -template -static bool Register() { - bool b = true; - b = b && Register(); // Call the above function - b = b && Register(); // Call ourself again (recursively) - return b; -} -#define EMITTER_OPCODE_TABLE(name, ...) \ - static bool Registered_##name = Register<__VA_ARGS__>(); - // ============================================================================ // OPCODE_COMMENT // ============================================================================ diff --git a/src/xenia/cpu/backend/x64/x64_sequences.h b/src/xenia/cpu/backend/x64/x64_sequences.h index a0103fca5..edb483022 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.h +++ b/src/xenia/cpu/backend/x64/x64_sequences.h @@ -12,6 +12,8 @@ #include "xenia/cpu/hir/instr.h" +#include + namespace xe { namespace cpu { namespace backend { @@ -19,6 +21,25 @@ namespace x64 { class X64Emitter; +typedef bool (*SequenceSelectFn)(X64Emitter&, const hir::Instr*); +extern std::unordered_map sequence_table; + +template +bool Register() { + sequence_table.insert({T::head_key(), T::Select}); + return true; +} + +template +static bool Register() { + bool b = true; + b = b && Register(); // Call the above function + b = b && Register(); // Call ourself again (recursively) + return b; +} +#define EMITTER_OPCODE_TABLE(name, ...) \ + const auto X64_INSTR_##name = Register<__VA_ARGS__>(); + void RegisterSequences(); bool SelectSequence(X64Emitter* e, const hir::Instr* i, const hir::Instr** new_tail);