diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index 6a5880d5f..9c30fe964 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -465,6 +465,7 @@ struct Sequence { 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())); @@ -478,6 +479,7 @@ struct Sequence { 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())); @@ -7588,6 +7590,25 @@ struct ATOMIC_COMPARE_EXCHANGE_I64 EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_COMPARE_EXCHANGE, ATOMIC_COMPARE_EXCHANGE_I32, ATOMIC_COMPARE_EXCHANGE_I64); +// ============================================================================ +// OPCODE_SET_ROUNDING_MODE +// ============================================================================ +// Input: FPSCR (PPC format) +static const uint32_t mxcsr_table[] = { + 0x1F80, 0x7F80, 0x5F80, 0x3F80, 0x9F80, 0xFF80, 0xDF80, 0xBF80, +}; +struct SET_ROUNDING_MODE_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.mov(e.rcx, i.src1); + e.and_(e.rcx, 0x7); + e.mov(e.rax, uintptr_t(mxcsr_table)); + e.vldmxcsr(e.ptr[e.rax + e.rcx * 4]); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_SET_ROUNDING_MODE, SET_ROUNDING_MODE_I32); + void RegisterSequences() { Register_OPCODE_COMMENT(); Register_OPCODE_NOP(); @@ -7706,6 +7727,7 @@ void RegisterSequences() { Register_OPCODE_UNPACK(); Register_OPCODE_ATOMIC_EXCHANGE(); Register_OPCODE_ATOMIC_COMPARE_EXCHANGE(); + Register_OPCODE_SET_ROUNDING_MODE(); } bool SelectSequence(X64Emitter* e, const Instr* i, const Instr** new_tail) { diff --git a/src/xenia/cpu/hir/hir_builder.cc b/src/xenia/cpu/hir/hir_builder.cc index ccb5d4b57..a654ed0ba 100644 --- a/src/xenia/cpu/hir/hir_builder.cc +++ b/src/xenia/cpu/hir/hir_builder.cc @@ -1269,6 +1269,12 @@ void HIRBuilder::Prefetch(Value* address, size_t length, void HIRBuilder::MemoryBarrier() { AppendInstr(OPCODE_MEMORY_BARRIER_info, 0); } +void HIRBuilder::SetRoundingMode(Value* value) { + ASSERT_INTEGER_TYPE(value); + Instr* i = AppendInstr(OPCODE_SET_ROUNDING_MODE_info, 0); + i->set_src1(value); +} + Value* HIRBuilder::Max(Value* value1, Value* value2) { ASSERT_TYPES_EQUAL(value1, value2); diff --git a/src/xenia/cpu/hir/hir_builder.h b/src/xenia/cpu/hir/hir_builder.h index 319630a5d..41fbf7c1e 100644 --- a/src/xenia/cpu/hir/hir_builder.h +++ b/src/xenia/cpu/hir/hir_builder.h @@ -153,6 +153,7 @@ class HIRBuilder { void Prefetch(Value* address, size_t length, uint32_t prefetch_flags = 0); void MemoryBarrier(); + void SetRoundingMode(Value* value); Value* Max(Value* value1, Value* value2); Value* VectorMax(Value* value1, Value* value2, TypeName part_type, uint32_t arithmetic_flags = 0); diff --git a/src/xenia/cpu/hir/opcodes.h b/src/xenia/cpu/hir/opcodes.h index 04206840e..6ae58f8f4 100644 --- a/src/xenia/cpu/hir/opcodes.h +++ b/src/xenia/cpu/hir/opcodes.h @@ -225,6 +225,7 @@ enum Opcode { OPCODE_UNPACK, OPCODE_ATOMIC_EXCHANGE, OPCODE_ATOMIC_COMPARE_EXCHANGE, + OPCODE_SET_ROUNDING_MODE, __OPCODE_MAX_VALUE, // Keep at end. }; diff --git a/src/xenia/cpu/hir/opcodes.inl b/src/xenia/cpu/hir/opcodes.inl index 1516d7c09..9930cfe8d 100644 --- a/src/xenia/cpu/hir/opcodes.inl +++ b/src/xenia/cpu/hir/opcodes.inl @@ -649,3 +649,9 @@ DEFINE_OPCODE( "atomic_compare_exchange", OPCODE_SIG_V_V_V_V, OPCODE_FLAG_VOLATILE) + +DEFINE_OPCODE( + OPCODE_SET_ROUNDING_MODE, + "set_rounding_mode", + OPCODE_SIG_X_V, + 0)