aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--decoder/include/berberis/decoder/riscv64/semantics_player.h24
-rw-r--r--test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h54
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}});