diff options
-rw-r--r-- | decoder/include/berberis/decoder/riscv64/semantics_player.h | 24 | ||||
-rw-r--r-- | test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h | 54 |
2 files changed, 75 insertions, 3 deletions
diff --git a/decoder/include/berberis/decoder/riscv64/semantics_player.h b/decoder/include/berberis/decoder/riscv64/semantics_player.h index 7f4a20c0..eedb6fdf 100644 --- a/decoder/include/berberis/decoder/riscv64/semantics_player.h +++ b/decoder/include/berberis/decoder/riscv64/semantics_player.h @@ -610,9 +610,25 @@ class SemanticsPlayer { int8_t dst, int8_t src1, int8_t src2) { - FpRegister arg1 = GetFRegAndUnboxNan<FloatType>(src1); - FpRegister arg2 = GetFRegAndUnboxNan<FloatType>(src2); + FpRegister arg1; + FpRegister arg2; FpRegister result; + // The sign-injection instructions (FSGNJ, FSGNJN, FSGNJX) do not canonicalize NaNs; + // they manipulate the underlying bit patterns directly. + bool canonicalize_nan = true; + switch (opcode) { + case Decoder::OpFpNoRoundingOpcode::kFSgnj: + case Decoder::OpFpNoRoundingOpcode::kFSgnjn: + case Decoder::OpFpNoRoundingOpcode::kFSgnjx: + arg1 = GetFpReg(src1); + arg2 = GetFpReg(src2); + canonicalize_nan = false; + break; + default: + // Unboxing canonicalizes NaNs. + arg1 = GetFRegAndUnboxNan<FloatType>(src1); + arg2 = GetFRegAndUnboxNan<FloatType>(src2); + } switch (opcode) { case Decoder::OpFpNoRoundingOpcode::kFSgnj: result = listener_->template FSgnj<FloatType>(arg1, arg2); @@ -633,7 +649,9 @@ class SemanticsPlayer { Undefined(); return; } - result = CanonicalizeNan<FloatType>(result); + if (canonicalize_nan) { + result = CanonicalizeNan<FloatType>(result); + } NanBoxAndSetFpReg<FloatType>(dst, result); } diff --git a/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h b/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h index 379d167f..6bf8a52c 100644 --- a/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h +++ b/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h @@ -1957,6 +1957,60 @@ TEST_F(TESTSUITE, Fmv) { {std::tuple{bit_cast<uint64_t>(1.0), 1.0}, {bit_cast<uint64_t>(-1.0), -1.0}}); } +const uint32_t kPosNanFloat = kFPValueToFPReg(std::numeric_limits<float>::quiet_NaN()); +const uint32_t kNegNanFloat = kFPValueToFPReg(-std::numeric_limits<float>::quiet_NaN()); +const uint64_t kPosNanDouble = kFPValueToFPReg(std::numeric_limits<double>::quiet_NaN()); +const uint64_t kNegNanDouble = kFPValueToFPReg(-std::numeric_limits<double>::quiet_NaN()); +constexpr uint64_t kMaskFloatBits = (uint64_t{1} << 32) - 1; + +TEST_F(TESTSUITE, FabsSinglePrecisionNanPosToPos) { + SetFReg<2>(state_.cpu, kPosNanFloat); + RunInstruction(0x202120d3); // fabs.s f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu) & kMaskFloatBits, kPosNanFloat); +} + +TEST_F(TESTSUITE, FabsSinglePrecisionNanNegToPos) { + SetFReg<2>(state_.cpu, kNegNanFloat); + RunInstruction(0x202120d3); // fabs.s f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu) & kMaskFloatBits, kPosNanFloat); +} + +TEST_F(TESTSUITE, FabsDoublePrecisionNanPosToPos) { + SetFReg<2>(state_.cpu, kPosNanDouble); + RunInstruction(0x222120d3); // fabs.d f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu), kPosNanDouble); +} + +TEST_F(TESTSUITE, FabsDoublePrecisionNanNegToPos) { + SetFReg<2>(state_.cpu, kNegNanDouble); + RunInstruction(0x222120d3); // fabs.d f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu), kPosNanDouble); +} + +TEST_F(TESTSUITE, FnegSinglePrecisionNanPosToNeg) { + SetFReg<2>(state_.cpu, kPosNanFloat); + RunInstruction(0x202110d3); // fneg.s f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu) & kMaskFloatBits, kNegNanFloat); +} + +TEST_F(TESTSUITE, FnegSinglePrecisionNanNegToPos) { + SetFReg<2>(state_.cpu, kNegNanFloat); + RunInstruction(0x202110d3); // fneg.s f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu) & kMaskFloatBits, kPosNanFloat); +} + +TEST_F(TESTSUITE, FnegDoublePrecisionNanPosToNeg) { + SetFReg<2>(state_.cpu, kPosNanDouble); + RunInstruction(0x222110d3); // fneg.s f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu), kNegNanDouble); +} + +TEST_F(TESTSUITE, FnegDoublePrecisionNanNegToPos) { + SetFReg<2>(state_.cpu, kNegNanDouble); + RunInstruction(0x222110d3); // fneg.s f1, f2 + EXPECT_EQ(GetFReg<1>(state_.cpu), kPosNanDouble); +} + TEST_F(TESTSUITE, OpFpFcvt) { // Fcvt.S.D TestOpFpSingleInput(0x401170d3, {std::tuple{1.0, 1.0f}}); |