diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index 4f3730c8b..e12eaf1da 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -2909,6 +2909,24 @@ struct IS_FALSE_V128 EMITTER_OPCODE_TABLE(OPCODE_IS_FALSE, IS_FALSE_I8, IS_FALSE_I16, IS_FALSE_I32, IS_FALSE_I64, IS_FALSE_F32, IS_FALSE_F64, IS_FALSE_V128); +// ============================================================================ +// OPCODE_IS_NAN +// ============================================================================ +struct IS_NAN_F32 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.vucomiss(i.src1, i.src1); + e.setp(i.dest); + } +}; + +struct IS_NAN_F64 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.vucomisd(i.src1, i.src1); + e.setp(i.dest); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_IS_NAN, IS_NAN_F32, IS_NAN_F64); + // ============================================================================ // OPCODE_COMPARE_EQ // ============================================================================ @@ -7566,6 +7584,7 @@ void RegisterSequences() { Register_OPCODE_SELECT(); Register_OPCODE_IS_TRUE(); Register_OPCODE_IS_FALSE(); + Register_OPCODE_IS_NAN(); Register_OPCODE_COMPARE_EQ(); Register_OPCODE_COMPARE_NE(); Register_OPCODE_COMPARE_SLT(); diff --git a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc index 40a50d5e5..1d92e462b 100644 --- a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc +++ b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc @@ -300,6 +300,20 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { i->Remove(); } break; + case OPCODE_IS_NAN: + if (i->src1.value->IsConstant()) { + if (i->src1.value->type == FLOAT32_TYPE && + isnan(i->src1.value->constant.f32)) { + v->set_constant(uint8_t(1)); + } else if (i->src1.value->type == FLOAT64_TYPE && + isnan(i->src1.value->constant.f64)) { + v->set_constant(uint8_t(1)); + } else { + v->set_constant(uint8_t(0)); + } + i->Remove(); + } + break; // TODO(benvanik): compares case OPCODE_COMPARE_EQ: diff --git a/src/xenia/cpu/hir/hir_builder.cc b/src/xenia/cpu/hir/hir_builder.cc index 774599215..ccb5d4b57 100644 --- a/src/xenia/cpu/hir/hir_builder.cc +++ b/src/xenia/cpu/hir/hir_builder.cc @@ -1362,6 +1362,13 @@ Value* HIRBuilder::IsFalse(Value* value) { return i->dest; } +Value* HIRBuilder::IsNan(Value* value) { + Instr* i = AppendInstr(OPCODE_IS_NAN_info, 0, AllocValue(INT8_TYPE)); + i->set_src1(value); + i->src2.value = i->src3.value = NULL; + return i->dest; +} + Value* HIRBuilder::CompareXX(const OpcodeInfo& opcode, 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 a596ddfbe..319630a5d 100644 --- a/src/xenia/cpu/hir/hir_builder.h +++ b/src/xenia/cpu/hir/hir_builder.h @@ -162,6 +162,7 @@ class HIRBuilder { Value* Select(Value* cond, Value* value1, Value* value2); Value* IsTrue(Value* value); Value* IsFalse(Value* value); + Value* IsNan(Value* value); Value* CompareEQ(Value* value1, Value* value2); Value* CompareNE(Value* value1, Value* value2); Value* CompareSLT(Value* value1, Value* value2); diff --git a/src/xenia/cpu/hir/opcodes.h b/src/xenia/cpu/hir/opcodes.h index 7e127ac32..8d55f0e0b 100644 --- a/src/xenia/cpu/hir/opcodes.h +++ b/src/xenia/cpu/hir/opcodes.h @@ -164,6 +164,7 @@ enum Opcode { OPCODE_SELECT, OPCODE_IS_TRUE, OPCODE_IS_FALSE, + OPCODE_IS_NAN, OPCODE_COMPARE_EQ, OPCODE_COMPARE_NE, OPCODE_COMPARE_SLT, diff --git a/src/xenia/cpu/hir/opcodes.inl b/src/xenia/cpu/hir/opcodes.inl index a49fb4a5e..1516d7c09 100644 --- a/src/xenia/cpu/hir/opcodes.inl +++ b/src/xenia/cpu/hir/opcodes.inl @@ -303,6 +303,12 @@ DEFINE_OPCODE( OPCODE_SIG_V_V, 0) +DEFINE_OPCODE( + OPCODE_IS_NAN, + "is_nan", + OPCODE_SIG_V_V, + 0) + DEFINE_OPCODE( OPCODE_COMPARE_EQ, "compare_eq",