zfh: add nan boxing to fmv.h.x and properly handle 32 & 64 bit XLEN PiperOrigin-RevId: 759377568 Change-Id: I6dd3146a3e8c394878d5339d6c8ce1a4cf837c81
diff --git a/riscv/riscv_zfh.isa b/riscv/riscv_zfh.isa index 33d15cc..a6ecf23 100644 --- a/riscv/riscv_zfh.isa +++ b/riscv/riscv_zfh.isa
@@ -35,10 +35,6 @@ disasm: "Unimplemented instruction at 0x%(@:08x)", semfunc: "&RV32VUnimplementedInstruction"; opcodes { - fmv_hx{: rs1 : frd}, - resources: {next_pc, rs1 : frd[0..]}, - semfunc: "&RiscVZfhFMvhx", - disasm: "fmv.h.x", "%frd, %rs1"; fcvt_sh{: frs1, rm : frd, fflags}, resources: {next_pc, frs1 : frd[0..]}, semfunc: "&RiscVZfhCvtSh", @@ -81,6 +77,10 @@ resources: {next_pc, frs1 : rd[0..]}, semfunc: "&RV32::RiscVZfhFMvxh", disasm: "fmv.x.h", "%rd, %frs1"; + fmv_hx{: rs1 : frd}, + resources: {next_pc, rs1 : frd[0..]}, + semfunc: "&RV32::RiscVZfhFMvhx", + disasm: "fmv.h.x", "%frd, %rs1"; } } @@ -107,6 +107,10 @@ resources: {next_pc, frs1 : rd[0..]}, semfunc: "&RV64::RiscVZfhFMvxh", disasm: "fmv.x.h", "%rd, %frs1"; + fmv_hx{: rs1 : frd}, + resources: {next_pc, rs1 : frd[0..]}, + semfunc: "&RV64::RiscVZfhFMvhx", + disasm: "fmv.h.x", "%frd, %rs1"; } }
diff --git a/riscv/riscv_zfh_instructions.cc b/riscv/riscv_zfh_instructions.cc index 484173e..8432321 100644 --- a/riscv/riscv_zfh_instructions.cc +++ b/riscv/riscv_zfh_instructions.cc
@@ -514,6 +514,27 @@ }); } +// Move a half precision value from an integer register to a float register and +// NaN box the value. +void RiscVZfhFMvhx(const Instruction *instruction) { + using DstRegValue = RVFpRegister::ValueType; + using SrcRegValue = RV32Register::ValueType; + SrcRegValue lhs = generic::GetInstructionSource<SrcRegValue>(instruction, 0); + HalfFP dest_value = {.value = static_cast<uint16_t>(lhs)}; + + auto *reg = static_cast<generic::RegisterDestinationOperand<DstRegValue> *>( + instruction->Destination(0)) + ->GetRegister(); + + using UReg = typename std::make_unsigned<DstRegValue>::type; + using UInt = typename FPTypeInfo<HalfFP>::UIntType; + auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value); + UReg reg_value = std::numeric_limits<UReg>::max(); + int shift = 8 * sizeof(HalfFP); + reg_value = (reg_value << shift) | dest_u_value; + reg->data_buffer()->template Set<DstRegValue>(0, reg_value); +} + // Convert from half precision to integer. void RiscVZfhCvtWh(const Instruction *instruction) { RiscVConvertFloatWithFflagsOp<typename RV32Register::ValueType, HalfFP, @@ -632,6 +653,27 @@ }); } +// Move a half precision value from an integer register to a float register and +// NaN box the value. +void RiscVZfhFMvhx(const Instruction *instruction) { + using DstRegValue = RVFpRegister::ValueType; + using SrcRegValue = RV64Register::ValueType; + SrcRegValue lhs = generic::GetInstructionSource<SrcRegValue>(instruction, 0); + HalfFP dest_value = {.value = static_cast<uint16_t>(lhs)}; + + auto *reg = static_cast<generic::RegisterDestinationOperand<DstRegValue> *>( + instruction->Destination(0)) + ->GetRegister(); + + using UReg = typename std::make_unsigned<DstRegValue>::type; + using UInt = typename FPTypeInfo<HalfFP>::UIntType; + auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value); + UReg reg_value = std::numeric_limits<UReg>::max(); + int shift = 8 * sizeof(HalfFP); + reg_value = (reg_value << shift) | dest_u_value; + reg->data_buffer()->template Set<DstRegValue>(0, reg_value); +} + } // namespace RV64 void RiscVZfhFlhChild(const Instruction *instruction) { @@ -654,13 +696,6 @@ reg->data_buffer()->Set<RVFpRegister::ValueType>(0, value); } -// Move a half precision value from an integer register to a float register. -void RiscVZfhFMvhx(const Instruction *instruction) { - RiscVUnaryFloatOp<HalfFP, uint64_t>(instruction, [](uint64_t a) -> HalfFP { - return HalfFP{.value = static_cast<uint16_t>(a)}; - }); -} - // Convert from half precision to single precision. void RiscVZfhCvtSh(const Instruction *instruction) { RiscVZfhCvtHelper<float, HalfFP>(
diff --git a/riscv/riscv_zfh_instructions.h b/riscv/riscv_zfh_instructions.h index 20d5261..c4e3975 100644 --- a/riscv/riscv_zfh_instructions.h +++ b/riscv/riscv_zfh_instructions.h
@@ -33,6 +33,12 @@ void RiscVZfhFMvxh(const Instruction *instruction); // Source Operands: +// rs1: Integer Register +// Destination Operands: +// frd: Float Register +void RiscVZfhFMvhx(const Instruction *instruction); + +// Source Operands: // frs1: Float Register // rm: Literal Operand (rounding mode) // Destination Operands: @@ -102,18 +108,18 @@ // Destination Operands: // rd: Integer Register void RiscVZfhFMvxh(const Instruction *instruction); -} // namespace RV64 - -// Source Operands: *none* -// Destination Operands: -// frd: Float Register -void RiscVZfhFlhChild(const Instruction *instruction); // Source Operands: // rs1: Integer Register // Destination Operands: // frd: Float Register void RiscVZfhFMvhx(const Instruction *instruction); +} // namespace RV64 + +// Source Operands: *none* +// Destination Operands: +// frd: Float Register +void RiscVZfhFlhChild(const Instruction *instruction); // Source Operands: // frs1: Float Register
diff --git a/riscv/test/riscv_zfh_instructions_test.cc b/riscv/test/riscv_zfh_instructions_test.cc index dee6408..4c01542 100644 --- a/riscv/test/riscv_zfh_instructions_test.cc +++ b/riscv/test/riscv_zfh_instructions_test.cc
@@ -70,7 +70,6 @@ using ::mpact::sim::riscv::RiscVZfhFmin; using ::mpact::sim::riscv::RiscVZfhFmsub; using ::mpact::sim::riscv::RiscVZfhFmul; -using ::mpact::sim::riscv::RiscVZfhFMvhx; using ::mpact::sim::riscv::RiscVZfhFnmadd; using ::mpact::sim::riscv::RiscVZfhFnmsub; using ::mpact::sim::riscv::RiscVZfhFsgnj; @@ -92,7 +91,6 @@ using ::mpact::sim::riscv::RV32::RiscVZfhFcmpeq; using ::mpact::sim::riscv::RV32::RiscVZfhFcmple; using ::mpact::sim::riscv::RV32::RiscVZfhFcmplt; -using ::mpact::sim::riscv::RV32::RiscVZfhFMvxh; using ::mpact::sim::riscv::test::FloatingPointToString; using ::mpact::sim::riscv::test::FPCompare; @@ -167,6 +165,9 @@ std::function<std::tuple<R, uint32_t>(LHS, MHS, RHS)> operation); uint32_t GetOperationFlags(std::function<void(void)> operation); + + template <typename ScalarRegister, typename FPType> + void FmvHxHelper(); }; template <typename AddressType, typename ValueType> @@ -440,6 +441,24 @@ } } +template <typename ScalarRegister, typename FPType> +void RVZfhInstructionTestBase::FmvHxHelper() { + using ScalarRegisterType = ScalarRegister::ValueType; + AppendRegisterOperands<RVFpRegister>({}, {"f5"}); + AppendRegisterOperands<ScalarRegister>({"x5"}, {}); + instruction_->AppendSource(new TestRoundingModeSourceOperand()); + for (int i = 0; i < 128; i++) { + ScalarRegisterType scalar = absl::Uniform<ScalarRegisterType>(bitgen_); + SetRegisterValues<ScalarRegisterType, ScalarRegister>({{"x5", scalar}}); + SetRegisterValues<uint64_t, RVFpRegister>({{"f5", 0}}); + instruction_->Execute(nullptr); + FPType observed_fp = state_->GetRegister<RVFpRegister>("f5") + .first->data_buffer() + ->template Get<FPType>(0); + EXPECT_TRUE(std::isnan(observed_fp)); + } +} + class RV32ZfhInstructionTest : public RVZfhInstructionTestBase { protected: // Test conversion instructions. The instance variable semantic_function_ is @@ -566,7 +585,7 @@ // Move half precision from a float register to an integer register. The IEEE754 // encoding is preserved in the integer register. TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvxh) { - SetSemanticFunction(&RiscVZfhFMvxh); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvxh); UnaryOpFPTestHelper<uint32_t, HalfFP>( "fmv.x.h", instruction_, {"f", "x"}, 32, [](HalfFP half_fp) -> uint32_t { bool sign = 1 & (half_fp.value >> (FPTypeInfo<HalfFP>::kBitSize - 1)); @@ -580,13 +599,27 @@ // Move half precision from an integer register (lower 16 bits) to a float // register. TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvhx) { - SetSemanticFunction(&RiscVZfhFMvhx); - UnaryOpFPTestHelper<HalfFP, uint64_t>( - "fmv.h.x", instruction_, {"x", "f"}, 32, [](uint64_t scalar) -> HalfFP { + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvhx); + UnaryOpFPTestHelper<HalfFP, uint32_t>( + "fmv.h.x", instruction_, {"x", "f"}, 32, [](uint32_t scalar) -> HalfFP { return HalfFP{.value = static_cast<uint16_t>(scalar)}; }); } +// Move half precision from an integer register to a float register. Confirm +// that the destination value is NaN boxed. +TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvhx_float_nanbox) { + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvhx); + FmvHxHelper<RV32Register, float>(); +} + +// Move half precision from an integer register to a float register. Confirm +// that the destination value is NaN boxed. +TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvhx_double_nanbox) { + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvhx); + FmvHxHelper<RV32Register, double>(); +} + // Half precision to single precision conversion. TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtSh) { SetSemanticFunction(&RiscVZfhCvtSh); @@ -1458,6 +1491,30 @@ }); } +// Move half precision from an integer register (lower 16 bits) to a float +// register. +TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvhx) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFMvhx); + UnaryOpFPTestHelper<HalfFP, uint64_t>( + "fmv.h.x", instruction_, {"x", "f"}, 32, [](uint64_t scalar) -> HalfFP { + return HalfFP{.value = static_cast<uint16_t>(scalar)}; + }); +} + +// Move half precision from an integer register to a float register. Confirm +// that the destination value is NaN boxed. +TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvhx_float_nanbox) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFMvhx); + FmvHxHelper<RV64Register, float>(); +} + +// Move half precision from an integer register to a float register. Confirm +// that the destination value is NaN boxed. +TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvhx_double_nanbox) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFMvhx); + FmvHxHelper<RV64Register, double>(); +} + // Test the FP16 load instruction. The semantic functions should match the isa // file. TEST_F(RV64ZfhInstructionTest, RiscVFlh) {