zfh: Support both XLENs for all semantic functions that touch a scalar regsiter PiperOrigin-RevId: 760766479 Change-Id: I9e399582977f433835c342618e8b95fe39033096
diff --git a/riscv/riscv_zfh.isa b/riscv/riscv_zfh.isa index a6ecf23..76265d5 100644 --- a/riscv/riscv_zfh.isa +++ b/riscv/riscv_zfh.isa
@@ -25,7 +25,7 @@ // First disasm field is 18 char wide and left justified. disasm widths = {-18}; -slot riscv_zfh_min { +slot riscv_zfh_min_common { includes { #include "riscv/riscv_zfh_instructions.h" } @@ -54,7 +54,7 @@ } } -slot riscv32_zfh_min: riscv_zfh_min { +slot riscv32_zfh_min: riscv_zfh_min_common { includes { #include "riscv/riscv_i_instructions.h" #include "riscv/riscv_zfh_instructions.h" @@ -84,7 +84,7 @@ } } -slot riscv64_zfh_min: riscv_zfh_min { +slot riscv64_zfh_min: riscv_zfh_min_common { includes { #include "riscv/riscv_i_instructions.h" #include "riscv/riscv_zfh_instructions.h" @@ -114,7 +114,7 @@ } } -slot riscv32_zfh : riscv32_zfh_min { +slot riscv_zfh_common { includes { #include "riscv/riscv_zfh_instructions.h" } @@ -124,6 +124,14 @@ disasm: "Unimplemented instruction at 0x%(@:08x)", semfunc: "&RV32VUnimplementedInstruction"; opcodes { + fcvt_hw{: rs1, rm : frd, fflags}, + resources: {next_pc, frs1 : frd[0..]}, + semfunc: "&RiscVZfhCvtHw", + disasm: "fcvt.h.w", "%frd, %rs1"; + fcvt_hwu{: rs1, rm : frd, fflags}, + resources: {next_pc, frs1 : frd[0..]}, + semfunc: "&RiscVZfhCvtHwu", + disasm: "fcvt.h.wu", "%frd, %rs1"; fadd_h{: frs1, frs2, rm : frd, fflags}, resources: {next_pc, frs1, frs2 : frd[0..]}, semfunc: "&RiscVZfhFadd", @@ -152,22 +160,6 @@ resources: {next_pc, frs1 : frd[0..]}, semfunc: "&RiscVZfhFsqrt", disasm: "fsqrt.h", "%frd, %frs1"; - fcvt_hw{: rs1, rm : frd, fflags}, - resources: {next_pc, frs1 : frd[0..]}, - semfunc: "&RV32::RiscVZfhCvtHw", - disasm: "fcvt.h.w", "%frd, %rs1"; - fcvt_wh{: frs1, rm : rd, fflags}, - resources: {next_pc, frs1 : rd[0..]}, - semfunc: "&RV32::RiscVZfhCvtWh", - disasm: "fcvt.w.h", "%rd, %frs1"; - fcvt_hwu{: rs1, rm : frd, fflags}, - resources: {next_pc, frs1 : frd[0..]}, - semfunc: "&RV32::RiscVZfhCvtHwu", - disasm: "fcvt.h.wu", "%frd, %rs1"; - fcvt_wuh{: frs1, rm : rd, fflags}, - resources: {next_pc, frs1 : rd[0..]}, - semfunc: "&RV32::RiscVZfhCvtWuh", - disasm: "fcvt.wu.h", "%rd, %frs1"; fsgnj_h{: frs1, frs2 : frd }, resources: {next_pc, frs1, frs2 : frd[0..]}, semfunc: "&RiscVZfhFsgnj", @@ -180,22 +172,6 @@ resources: {next_pc, frs1, frs2 : frd[0..]}, semfunc: "&RiscVZfhFsgnjx", disasm: "fsgnjnx.h", "%frd, %frs1, %frs2"; - fcmpeq_h{: frs1, frs2 : rd, fflags}, - resources: { next_pc, frs1, frs2 : rd[0..]}, - semfunc: "&RV32::RiscVZfhFcmpeq", - disasm: "feq.h", "%rd, %frs1, %frs2"; - fcmplt_h{: frs1, frs2 : rd, fflags}, - resources: { next_pc, frs1, frs2 : rd[0..]}, - semfunc: "&RV32::RiscVZfhFcmplt", - disasm: "flt.h", "%rd, %frs1, %frs2"; - fcmple_h{: frs1, frs2 : rd, fflags}, - resources: { next_pc, frs1, frs2 : rd[0..]}, - semfunc: "&RV32::RiscVZfhFcmple", - disasm: "fle.h", "%rd, %frs1, %frs2"; - fclass_h{: frs1 : rd}, - resources: { next_pc, frs1 : rd[0..]}, - semfunc: "&RV32::RiscVZfhFclass", - disasm: "fclass.h", "%rd, %frs1"; fmadd_h{: frs1, frs2, frs3, rm : frd, fflags}, resources: {next_pc, frs1, frs2, frs3 : frd[0..]}, semfunc: "&RiscVZfhFmadd", @@ -214,3 +190,77 @@ disasm: "fnmsub.h", "%frd, %frs1, %frs2, %frs3"; } } + +slot riscv32_zfh : riscv32_zfh_min, riscv_zfh_common { + includes { + #include "riscv/riscv_zfh_instructions.h" + } + default size = 4; + default latency = 0; + default opcode = + disasm: "Unimplemented instruction at 0x%(@:08x)", + semfunc: "&RV32VUnimplementedInstruction"; + opcodes { + fcvt_wh{: frs1, rm : rd, fflags}, + resources: {next_pc, frs1 : rd[0..]}, + semfunc: "&RV32::RiscVZfhCvtWh", + disasm: "fcvt.w.h", "%rd, %frs1"; + fcvt_wuh{: frs1, rm : rd, fflags}, + resources: {next_pc, frs1 : rd[0..]}, + semfunc: "&RV32::RiscVZfhCvtWuh", + disasm: "fcvt.wu.h", "%rd, %frs1"; + fcmpeq_h{: frs1, frs2 : rd, fflags}, + resources: { next_pc, frs1, frs2 : rd[0..]}, + semfunc: "&RV32::RiscVZfhFcmpeq", + disasm: "feq.h", "%rd, %frs1, %frs2"; + fcmplt_h{: frs1, frs2 : rd, fflags}, + resources: { next_pc, frs1, frs2 : rd[0..]}, + semfunc: "&RV32::RiscVZfhFcmplt", + disasm: "flt.h", "%rd, %frs1, %frs2"; + fcmple_h{: frs1, frs2 : rd, fflags}, + resources: { next_pc, frs1, frs2 : rd[0..]}, + semfunc: "&RV32::RiscVZfhFcmple", + disasm: "fle.h", "%rd, %frs1, %frs2"; + fclass_h{: frs1 : rd}, + resources: { next_pc, frs1 : rd[0..]}, + semfunc: "&RV32::RiscVZfhFclass", + disasm: "fclass.h", "%rd, %frs1"; + } +} + +slot riscv64_zfh : riscv64_zfh_min, riscv_zfh_common { + includes { + #include "riscv/riscv_zfh_instructions.h" + } + default size = 4; + default latency = 0; + default opcode = + disasm: "Unimplemented instruction at 0x%(@:08x)", + semfunc: "&RV32VUnimplementedInstruction"; + opcodes { + fcvt_wh{: frs1, rm : rd, fflags}, + resources: {next_pc, frs1 : rd[0..]}, + semfunc: "&RV64::RiscVZfhCvtWh", + disasm: "fcvt.w.h", "%rd, %frs1"; + fcvt_wuh{: frs1, rm : rd, fflags}, + resources: {next_pc, frs1 : rd[0..]}, + semfunc: "&RV64::RiscVZfhCvtWuh", + disasm: "fcvt.wu.h", "%rd, %frs1"; + fcmpeq_h{: frs1, frs2 : rd, fflags}, + resources: { next_pc, frs1, frs2 : rd[0..]}, + semfunc: "&RV64::RiscVZfhFcmpeq", + disasm: "feq.h", "%rd, %frs1, %frs2"; + fcmplt_h{: frs1, frs2 : rd, fflags}, + resources: { next_pc, frs1, frs2 : rd[0..]}, + semfunc: "&RV64::RiscVZfhFcmplt", + disasm: "flt.h", "%rd, %frs1, %frs2"; + fcmple_h{: frs1, frs2 : rd, fflags}, + resources: { next_pc, frs1, frs2 : rd[0..]}, + semfunc: "&RV64::RiscVZfhFcmple", + disasm: "fle.h", "%rd, %frs1, %frs2"; + fclass_h{: frs1 : rd}, + resources: { next_pc, frs1 : rd[0..]}, + semfunc: "&RV64::RiscVZfhFclass", + disasm: "fclass.h", "%rd, %frs1"; + } +}
diff --git a/riscv/riscv_zfh_instructions.cc b/riscv/riscv_zfh_instructions.cc index 8432321..23af707 100644 --- a/riscv/riscv_zfh_instructions.cc +++ b/riscv/riscv_zfh_instructions.cc
@@ -43,7 +43,6 @@ using HalfFP = ::mpact::sim::generic::HalfFP; using ::mpact::sim::generic::IsMpactFp; using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). -using ::mpact::sim::generic::IsMpactFp; namespace { @@ -498,27 +497,24 @@ fflags_dest->GetRiscVCsr()->SetBits(fflags); } -} // namespace - -namespace RV32 { - -// Move a half precision value from a float register to a 32 bit integer -// register. -void RiscVZfhFMvxh(const Instruction *instruction) { - RiscVUnaryFloatOp<uint32_t, HalfFP>(instruction, [](HalfFP a) -> uint32_t { +// Move a half precision value from a float register to an integer register. +template <typename XRegister> +void RiscVZfhFMvxhHelper(const Instruction *instruction) { + using XRegValue = typename XRegister::ValueType; + RiscVUnaryFloatOp<XRegValue, HalfFP>(instruction, [](HalfFP a) -> XRegValue { if (FPTypeInfo<HalfFP>::SignBit(a)) { // Repeat the sign bit for negative values. - return 0xFFFF'0000 | a.value; + return (std::numeric_limits<XRegValue>::max() << 16) | a.value; } - return static_cast<uint32_t>(a.value); + return static_cast<XRegValue>(a.value); }); } -// Move a half precision value from an integer register to a float register and -// NaN box the value. -void RiscVZfhFMvhx(const Instruction *instruction) { +// Move a half precision value from an integer register to a float register +template <typename XRegister> +inline void RiscVZfhFMvhxHelper(const Instruction *instruction) { using DstRegValue = RVFpRegister::ValueType; - using SrcRegValue = RV32Register::ValueType; + using SrcRegValue = XRegister::ValueType; SrcRegValue lhs = generic::GetInstructionSource<SrcRegValue>(instruction, 0); HalfFP dest_value = {.value = static_cast<uint16_t>(lhs)}; @@ -526,6 +522,7 @@ instruction->Destination(0)) ->GetRegister(); + // NaN box the value. using UReg = typename std::make_unsigned<DstRegValue>::type; using UInt = typename FPTypeInfo<HalfFP>::UIntType; auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value); @@ -535,33 +532,101 @@ reg->data_buffer()->template Set<DstRegValue>(0, reg_value); } -// Convert from half precision to integer. +// Compare two half precision values for equality. +template <typename XRegister> +inline void RiscVZfhFcmpeqHelper(const Instruction *instruction) { + using DstRegValue = XRegister::ValueType; + uint32_t fflags = 0; + HalfFP lhs = + GetNaNBoxedSource<RVFpRegister::ValueType, HalfFP>(instruction, 0); + HalfFP rhs = + GetNaNBoxedSource<RVFpRegister::ValueType, HalfFP>(instruction, 1); + float lhs_f = ConvertFromHalfFP<float>(lhs, fflags); + float rhs_f = ConvertFromHalfFP<float>(rhs, fflags); + + DstRegValue result = lhs_f == rhs_f ? 1 : 0; + auto *reg = static_cast<generic::RegisterDestinationOperand<DstRegValue> *>( + instruction->Destination(0)) + ->GetRegister(); + reg->data_buffer()->template Set<DstRegValue>(0, result); + + RiscVCsrDestinationOperand *fflags_dest = + static_cast<RiscVCsrDestinationOperand *>(instruction->Destination(1)); + fflags_dest->GetRiscVCsr()->SetBits(fflags & *FPExceptions::kInvalidOp); +} + +// Compare two half precision values for less than. +template <typename XRegister> +inline void RiscVZfhFcmpltHelper(const Instruction *instruction) { + using DstRegValue = XRegister::ValueType; + uint32_t unused_fflags = 0; + HalfFP lhs = + GetNaNBoxedSource<RVFpRegister::ValueType, HalfFP>(instruction, 0); + HalfFP rhs = + GetNaNBoxedSource<RVFpRegister::ValueType, HalfFP>(instruction, 1); + float lhs_f = ConvertFromHalfFP<float>(lhs, unused_fflags); + float rhs_f = ConvertFromHalfFP<float>(rhs, unused_fflags); + + DstRegValue result = lhs_f < rhs_f ? 1 : 0; + auto *reg = static_cast<generic::RegisterDestinationOperand<DstRegValue> *>( + instruction->Destination(0)) + ->GetRegister(); + reg->data_buffer()->template Set<DstRegValue>(0, result); + + RiscVCsrDestinationOperand *fflags_dest = + static_cast<RiscVCsrDestinationOperand *>(instruction->Destination(1)); + if (std::isnan(lhs_f) || std::isnan(rhs_f)) { + fflags_dest->GetRiscVCsr()->SetBits(*FPExceptions::kInvalidOp); + } +} + +// Compare two half precision values for less than or equal to. +template <typename XRegister> +void RiscVZfhFcmpleHelper(const Instruction *instruction) { + using DstRegValue = XRegister::ValueType; + uint32_t unused_fflags = 0; + HalfFP lhs = + GetNaNBoxedSource<RVFpRegister::ValueType, HalfFP>(instruction, 0); + HalfFP rhs = + GetNaNBoxedSource<RVFpRegister::ValueType, HalfFP>(instruction, 1); + float lhs_f = ConvertFromHalfFP<float>(lhs, unused_fflags); + float rhs_f = ConvertFromHalfFP<float>(rhs, unused_fflags); + + DstRegValue result = lhs_f <= rhs_f ? 1 : 0; + auto *reg = static_cast<generic::RegisterDestinationOperand<DstRegValue> *>( + instruction->Destination(0)) + ->GetRegister(); + reg->data_buffer()->template Set<DstRegValue>(0, result); + + RiscVCsrDestinationOperand *fflags_dest = + static_cast<RiscVCsrDestinationOperand *>(instruction->Destination(1)); + if (std::isnan(lhs_f) || std::isnan(rhs_f)) { + fflags_dest->GetRiscVCsr()->SetBits(*FPExceptions::kInvalidOp); + } +} + +} // namespace + +namespace RV32 { +// Move a half precision value from a float register to a 32 bit integer +// register. +void RiscVZfhFMvxh(const Instruction *instruction) { + RiscVZfhFMvxhHelper<RV32Register>(instruction); +} + +// Move a half precision value from an integer register to a float register and +// NaN box the value. +void RiscVZfhFMvhx(const Instruction *instruction) { + RiscVZfhFMvhxHelper<RV32Register>(instruction); +} + +// Convert from half precision to signed 32 bit integer. void RiscVZfhCvtWh(const Instruction *instruction) { RiscVConvertFloatWithFflagsOp<typename RV32Register::ValueType, HalfFP, int32_t>(instruction); } -// Convert from integer to half precision. -void RiscVZfhCvtHw(const Instruction *instruction) { - RiscVZfhCvtHelper<HalfFP, int32_t>( - instruction, - [](int32_t a, FPRoundingMode rm, uint32_t &fflags) -> HalfFP { - float input_float = static_cast<float>(a); - return ConvertToHalfFP(input_float, rm, fflags); - }); -} - -// Convert from unsigned integer to half precision. -void RiscVZfhCvtHwu(const Instruction *instruction) { - RiscVZfhCvtHelper<HalfFP, uint32_t>( - instruction, - [](uint32_t a, FPRoundingMode rm, uint32_t &fflags) -> HalfFP { - float input_float = static_cast<float>(a); - return ConvertToHalfFP(input_float, rm, fflags); - }); -} - -// Convert from half precision to unsigned integer. +// Convert from half precision to unsigned 32 bit integer. void RiscVZfhCvtWuh(const Instruction *instruction) { RiscVConvertFloatWithFflagsOp<typename RV32Register::ValueType, HalfFP, uint32_t>(instruction); @@ -569,65 +634,17 @@ // Compare two half precision values for equality. void RiscVZfhFcmpeq(const Instruction *instruction) { - RiscVBinaryFloatNaNBoxOp<RVFpRegister::ValueType, uint64_t, HalfFP>( - instruction, [](HalfFP a, HalfFP b) -> uint64_t { - float a_f; - float b_f; - uint32_t unused_fflags = 0; - if (FPTypeInfo<HalfFP>::IsSNaN(a)) { - a_f = absl::bit_cast<float>(FPTypeInfo<float>::kPosInf | 1); - } else { - a_f = ConvertFromHalfFP<float>(a, unused_fflags); - } - if (FPTypeInfo<HalfFP>::IsSNaN(b)) { - b_f = absl::bit_cast<float>(FPTypeInfo<float>::kPosInf | 1); - } else { - b_f = ConvertFromHalfFP<float>(b, unused_fflags); - } - return a_f == b_f ? 1 : 0; - }); + RiscVZfhFcmpeqHelper<RV32Register>(instruction); } // Compare two half precision values for less than. void RiscVZfhFcmplt(const Instruction *instruction) { - RiscVBinaryFloatNaNBoxOp<RVFpRegister::ValueType, uint64_t, HalfFP>( - instruction, [](HalfFP a, HalfFP b) -> uint64_t { - float a_f; - float b_f; - uint32_t unused_fflags = 0; - if (FPTypeInfo<HalfFP>::IsNaN(a)) { - a_f = absl::bit_cast<float>(FPTypeInfo<float>::kPosInf | 1); - } else { - a_f = ConvertFromHalfFP<float>(a, unused_fflags); - } - if (FPTypeInfo<HalfFP>::IsNaN(b)) { - b_f = absl::bit_cast<float>(FPTypeInfo<float>::kPosInf | 1); - } else { - b_f = ConvertFromHalfFP<float>(b, unused_fflags); - } - return a_f < b_f ? 1 : 0; - }); + RiscVZfhFcmpltHelper<RV32Register>(instruction); } // Compare two half precision values for less than or equal to. void RiscVZfhFcmple(const Instruction *instruction) { - RiscVBinaryFloatNaNBoxOp<RVFpRegister::ValueType, uint64_t, HalfFP>( - instruction, [](HalfFP a, HalfFP b) -> uint64_t { - float a_f; - float b_f; - uint32_t unused_fflags = 0; - if (FPTypeInfo<HalfFP>::IsNaN(a)) { - a_f = absl::bit_cast<float>(FPTypeInfo<float>::kPosInf | 1); - } else { - a_f = ConvertFromHalfFP<float>(a, unused_fflags); - } - if (FPTypeInfo<HalfFP>::IsNaN(b)) { - b_f = absl::bit_cast<float>(FPTypeInfo<float>::kPosInf | 1); - } else { - b_f = ConvertFromHalfFP<float>(b, unused_fflags); - } - return a_f <= b_f ? 1 : 0; - }); + RiscVZfhFcmpleHelper<RV32Register>(instruction); } // Classify a half precision value. @@ -644,34 +661,48 @@ // Move a half precision value from a float register to a 32 bit integer // register. void RiscVZfhFMvxh(const Instruction *instruction) { - RiscVUnaryFloatOp<uint64_t, HalfFP>(instruction, [](HalfFP a) -> uint64_t { - if (FPTypeInfo<HalfFP>::SignBit(a)) { - // Repeat the sign bit for negative values. - return 0xFFFF'FFFF'FFFF'0000 | a.value; - } - return static_cast<uint64_t>(a.value); - }); + RiscVZfhFMvxhHelper<RV64Register>(instruction); } // 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)}; + RiscVZfhFMvhxHelper<RV64Register>(instruction); +} - auto *reg = static_cast<generic::RegisterDestinationOperand<DstRegValue> *>( - instruction->Destination(0)) - ->GetRegister(); +// Convert from half precision to signed 32 bit integer. +void RiscVZfhCvtWh(const Instruction *instruction) { + RiscVConvertFloatWithFflagsOp<typename RV64Register::ValueType, HalfFP, + int32_t>(instruction); +} - 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 unsigned 32 bit integer. +void RiscVZfhCvtWuh(const Instruction *instruction) { + RiscVConvertFloatWithFflagsOp<typename RV64Register::ValueType, HalfFP, + uint32_t>(instruction); +} + +// Compare two half precision values for equality. +void RiscVZfhFcmpeq(const Instruction *instruction) { + RiscVZfhFcmpeqHelper<RV64Register>(instruction); +} + +// Compare two half precision values for less than. +void RiscVZfhFcmplt(const Instruction *instruction) { + RiscVZfhFcmpltHelper<RV64Register>(instruction); +} + +// Compare two half precision values for less than or equal to. +void RiscVZfhFcmple(const Instruction *instruction) { + RiscVZfhFcmpleHelper<RV64Register>(instruction); +} + +// Classify a half precision value. +void RiscVZfhFclass(const Instruction *instruction) { + RiscVUnaryOp<RV64Register, uint64_t, HalfFP>( + instruction, [](HalfFP a) -> uint64_t { + return static_cast<uint64_t>(ClassifyFP(a)); + }); } } // namespace RV64 @@ -863,6 +894,26 @@ [](float a, float b, float c) -> float { return fma(-a, b, c); }); } +// Convert from signed 32 bit integer to half precision. +void RiscVZfhCvtHw(const Instruction *instruction) { + RiscVZfhCvtHelper<HalfFP, int32_t>( + instruction, + [](int32_t a, FPRoundingMode rm, uint32_t &fflags) -> HalfFP { + float input_float = static_cast<float>(a); + return ConvertToHalfFP(input_float, rm, fflags); + }); +} + +// Convert from unsigned 32 bit integer to half precision. +void RiscVZfhCvtHwu(const Instruction *instruction) { + RiscVZfhCvtHelper<HalfFP, uint32_t>( + instruction, + [](uint32_t a, FPRoundingMode rm, uint32_t &fflags) -> HalfFP { + float input_float = static_cast<float>(a); + return ConvertToHalfFP(input_float, rm, fflags); + }); +} + // TODO(b/409778536): Factor out generic unimplemented instruction semantic // function. void RV32VUnimplementedInstruction(const Instruction *instruction) {
diff --git a/riscv/riscv_zfh_instructions.h b/riscv/riscv_zfh_instructions.h index c4e3975..f4e67c8 100644 --- a/riscv/riscv_zfh_instructions.h +++ b/riscv/riscv_zfh_instructions.h
@@ -47,22 +47,6 @@ void RiscVZfhCvtWh(const Instruction *instruction); // Source Operands: -// rs1: Integer Register -// rm: Literal Operand (rounding mode) -// Destination Operands: -// frd: Float Register -// fflags: Accrued Exception Flags field in FCSR -void RiscVZfhCvtHw(const Instruction *instruction); - -// Source Operands: -// rs1: Integer Register -// rm: Literal Operand (rounding mode) -// Destination Operands: -// frd: Float Register -// fflags: Accrued Exception Flags field in FCSR -void RiscVZfhCvtHwu(const Instruction *instruction); - -// Source Operands: // frs1: Float Register // rm: Literal Operand (rounding mode) // Destination Operands: @@ -114,6 +98,53 @@ // Destination Operands: // frd: Float Register void RiscVZfhFMvhx(const Instruction *instruction); + +// Source Operands: +// frs1: Float Register +// rm: Literal Operand (rounding mode) +// Destination Operands: +// rd: Integer Register +// fflags: Accrued Exception Flags field in FCSR +void RiscVZfhCvtWh(const Instruction *instruction); + +// Source Operands: +// frs1: Float Register +// rm: Literal Operand (rounding mode) +// Destination Operands: +// rd: Integer Register +// fflags: Accrued Exception Flags field in FCSR +void RiscVZfhCvtWuh(const Instruction *instruction); + +// Source Operands: +// frs1: Float Register +// frs2: Float Register +// Destination Operands: +// rd: Integer Register +// fflags: Accrued Exception Flags field in FCSR +void RiscVZfhFcmpeq(const Instruction *instruction); + +// Source Operands: +// frs1: Float Register +// frs2: Float Register +// Destination Operands: +// rd: Integer Register +// fflags: Accrued Exception Flags field in FCSR +void RiscVZfhFcmplt(const Instruction *instruction); + +// Source Operands: +// frs1: Float Register +// frs2: Float Register +// Destination Operands: +// rd: Integer Register +// fflags: Accrued Exception Flags field in FCSR +void RiscVZfhFcmple(const Instruction *instruction); + +// Source Operands: +// frs1: Float Register +// Destination Operands: +// rd: Integer Register +void RiscVZfhFclass(const Instruction *instruction); + } // namespace RV64 // Source Operands: *none* @@ -280,6 +311,22 @@ // fflags: Accrued Exception Flags field in FCSR void RiscVZfhFnmsub(const Instruction *instruction); +// Source Operands: +// rs1: Integer Register +// rm: Literal Operand (rounding mode) +// Destination Operands: +// frd: Float Register +// fflags: Accrued Exception Flags field in FCSR +void RiscVZfhCvtHw(const Instruction *instruction); + +// Source Operands: +// rs1: Integer Register +// rm: Literal Operand (rounding mode) +// Destination Operands: +// frd: Float Register +// fflags: Accrued Exception Flags field in FCSR +void RiscVZfhCvtHwu(const Instruction *instruction); + } // namespace riscv } // namespace sim } // namespace mpact
diff --git a/riscv/test/riscv_zfh_instructions_test.cc b/riscv/test/riscv_zfh_instructions_test.cc index 4c01542..1e80aa3 100644 --- a/riscv/test/riscv_zfh_instructions_test.cc +++ b/riscv/test/riscv_zfh_instructions_test.cc
@@ -61,6 +61,8 @@ using ::mpact::sim::riscv::RiscVZfhCvtDh; using ::mpact::sim::riscv::RiscVZfhCvtHd; using ::mpact::sim::riscv::RiscVZfhCvtHs; +using ::mpact::sim::riscv::RiscVZfhCvtHw; +using ::mpact::sim::riscv::RiscVZfhCvtHwu; using ::mpact::sim::riscv::RiscVZfhCvtSh; using ::mpact::sim::riscv::RiscVZfhFadd; using ::mpact::sim::riscv::RiscVZfhFdiv; @@ -82,15 +84,6 @@ using ::mpact::sim::riscv::RVFpRegister; using ::mpact::sim::riscv::ScopedFPRoundingMode; using ::mpact::sim::riscv::ScopedFPStatus; -using ::mpact::sim::riscv::RV32::RiscVILhu; -using ::mpact::sim::riscv::RV32::RiscVZfhCvtHw; -using ::mpact::sim::riscv::RV32::RiscVZfhCvtHwu; -using ::mpact::sim::riscv::RV32::RiscVZfhCvtWh; -using ::mpact::sim::riscv::RV32::RiscVZfhCvtWuh; -using ::mpact::sim::riscv::RV32::RiscVZfhFclass; -using ::mpact::sim::riscv::RV32::RiscVZfhFcmpeq; -using ::mpact::sim::riscv::RV32::RiscVZfhFcmple; -using ::mpact::sim::riscv::RV32::RiscVZfhFcmplt; using ::mpact::sim::riscv::test::FloatingPointToString; using ::mpact::sim::riscv::test::FPCompare; @@ -167,7 +160,28 @@ uint32_t GetOperationFlags(std::function<void(void)> operation); template <typename ScalarRegister, typename FPType> - void FmvHxHelper(); + void FmvHxNanBoxHelper(); + + template <typename ScalarRegister> + void FmvXhHelper(); + + template <typename ScalarRegister, typename SourceIntegerType> + void CvtHwHelper(absl::string_view); + + template <typename ScalarRegister, typename DestinationIntegerType> + void CvtWhHelper(absl::string_view); + + template <typename ScalarRegister> + void CmpEqHelper(); + + template <typename ScalarRegister> + void CmpLtHelper(); + + template <typename ScalarRegister> + void CmpLeHelper(); + + template <typename ScalarRegister> + void ClassHelper(); }; template <typename AddressType, typename ValueType> @@ -441,8 +455,9 @@ } } +// Verify that the fmv.h.x instruction NaN boxes the converted value. template <typename ScalarRegister, typename FPType> -void RVZfhInstructionTestBase::FmvHxHelper() { +void RVZfhInstructionTestBase::FmvHxNanBoxHelper() { using ScalarRegisterType = ScalarRegister::ValueType; AppendRegisterOperands<RVFpRegister>({}, {"f5"}); AppendRegisterOperands<ScalarRegister>({"x5"}, {}); @@ -459,6 +474,165 @@ } } +// Helper to test the movement of an IEEE encoded half precision value from a +// float register to an integer register. +template <typename ScalarRegister> +void RVZfhInstructionTestBase::FmvXhHelper() { + using ScalarRegisterType = ScalarRegister::ValueType; + UnaryOpFPTestHelper<ScalarRegisterType, HalfFP>( + "fmv.x.h", instruction_, {"f", "x"}, 32, + [](HalfFP half_fp) -> ScalarRegisterType { + bool sign = 1 & (half_fp.value >> (FPTypeInfo<HalfFP>::kBitSize - 1)); + // Fill the upper XLEN-16 bits with the sign bit as per the spec. + ScalarRegisterType result = + sign ? (std::numeric_limits<ScalarRegisterType>::max() << 16) : 0; + result |= static_cast<ScalarRegisterType>(half_fp.value); + return result; + }); +} + +// Helper to test conversion of a 32bit integer value to a half precision float +// value. +template <typename ScalarRegister, typename SourceIntegerType> +void RVZfhInstructionTestBase::CvtHwHelper(absl::string_view name) { + UnaryOpWithFflagsMixedTestHelper<RVFpRegister, ScalarRegister, HalfFP, + SourceIntegerType>( + name, instruction_, {"x", "f"}, 32, + [](SourceIntegerType input_int, int rm) -> std::tuple<HalfFP, uint32_t> { + uint32_t fflags = 0; + HalfFP result = FpConversionsTestHelper(static_cast<double>(input_int)) + .ConvertWithFlags<HalfFP>( + fflags, static_cast<FPRoundingMode>(rm)); + return std::make_tuple(result, fflags); + }); +} + +// Helper to test conversion of a half precision float value to a 32bit integer +// value. +template <typename ScalarRegister, typename DestinationIntegerType> +void RVZfhInstructionTestBase::CvtWhHelper(absl::string_view name) { + UnaryOpWithFflagsMixedTestHelper<ScalarRegister, RVFpRegister, + DestinationIntegerType, HalfFP>( + name, instruction_, {"f", "x"}, 32, + [this](HalfFP input, + int rm) -> std::tuple<DestinationIntegerType, uint32_t> { + uint32_t fflags = 0; + double input_double = + FpConversionsTestHelper(input).ConvertWithFlags<double>(fflags); + const DestinationIntegerType val = + RoundToInteger<double, DestinationIntegerType>(input_double, rm, + fflags); + return std::make_tuple(val, fflags); + }); +} + +// Helper to test the comparison of two half precision values for equality. +template <typename ScalarRegister> +void RVZfhInstructionTestBase::CmpEqHelper() { + using ScalarRegisterType = ScalarRegister::ValueType; + BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP, HalfFP>( + "feq.h", instruction_, {"f", "f", "x"}, 32, + [](HalfFP a, HalfFP b) -> std::tuple<ScalarRegisterType, uint32_t> { + uint32_t fflags = 0; + double a_f = + FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags); + double b_f = + FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags); + ScalarRegisterType result = a_f == b_f ? 1 : 0; + if (std::isnan(a_f) || std::isnan(b_f)) { + result = 0; + } + return std::make_tuple(result, fflags); + }); +} + +// Helper to test the comparison of two half precision values for less than. +template <typename ScalarRegister> +void RVZfhInstructionTestBase::CmpLtHelper() { + using ScalarRegisterType = ScalarRegister::ValueType; + BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP, HalfFP>( + "flt.h", instruction_, {"f", "f", "x"}, 32, + [](HalfFP a, HalfFP b) -> std::tuple<ScalarRegisterType, uint32_t> { + uint32_t fflags = 0; + double a_f = + FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags); + double b_f = + FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags); + ScalarRegisterType result = a_f < b_f ? 1 : 0; + if (std::isnan(a_f) || std::isnan(b_f)) { + result = 0; + // LT is a signaling comparison, so the invalid operation flag is + // set. + fflags |= static_cast<uint32_t>( + mpact::sim::riscv::FPExceptions::kInvalidOp); + } + return std::make_tuple(result, fflags); + }); +} + +// Helper to test the comparison of two half precision values for less than or +// equal to. +template <typename ScalarRegister> +void RVZfhInstructionTestBase::CmpLeHelper() { + using ScalarRegisterType = ScalarRegister::ValueType; + BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP, HalfFP>( + "fle.h", instruction_, {"f", "f", "x"}, 32, + [](HalfFP a, HalfFP b) -> std::tuple<ScalarRegisterType, uint32_t> { + uint32_t fflags = 0; + double a_f = + FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags); + double b_f = + FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags); + ScalarRegisterType result = a_f <= b_f ? 1 : 0; + if (std::isnan(a_f) || std::isnan(b_f)) { + result = 0; + // LE is a signaling comparison, so the invalid operation flag is + // set. + fflags |= static_cast<uint32_t>( + mpact::sim::riscv::FPExceptions::kInvalidOp); + } + return std::make_tuple(result, fflags); + }); +} + +// Helper to test the classification of the half precision value. +template <typename ScalarRegister> +void RVZfhInstructionTestBase::ClassHelper() { + using ScalarRegisterType = ScalarRegister::ValueType; + UnaryOpWithFflagsMixedTestHelper<ScalarRegister, RVFpRegister, + ScalarRegisterType, HalfFP>( + "fclass.h", instruction_, {"f", "x"}, 32, + [](HalfFP input, int rm) -> std::tuple<ScalarRegisterType, uint32_t> { + uint16_t sign_mask = + ~(FPTypeInfo<HalfFP>::kExpMask | FPTypeInfo<HalfFP>::kSigMask); + uint16_t sign = input.value & sign_mask; + int shift = -1; + + switch (input.value & FPTypeInfo<HalfFP>::kExpMask) { + case 0: + if (input.value & FPTypeInfo<HalfFP>::kSigMask) { + shift = sign ? 2 : 5; // +/- Subnormal + } else { + shift = sign ? 3 : 4; // +/- zero + } + break; + case FPTypeInfo<HalfFP>::kExpMask: + if (input.value & FPTypeInfo<HalfFP>::kSigMask) { + // Quiet/Signaling NaN + shift = FPTypeInfo<HalfFP>::IsQNaN(input) ? 9 : 8; + } else { + shift = sign ? 0 : 7; // +/- infinity + } + break; + default: + shift = sign ? 1 : 6; // +/- normal + break; + } + EXPECT_GE(shift, 0) << "The test didn't set the expected result."; + return std::make_tuple(static_cast<ScalarRegisterType>(1) << shift, 0); + }); +} + class RV32ZfhInstructionTest : public RVZfhInstructionTestBase { protected: // Test conversion instructions. The instance variable semantic_function_ is @@ -543,7 +717,7 @@ // Test the FP16 load instruction. The semantic functions should match the isa // file. TEST_F(RV32ZfhInstructionTest, RiscVFlh) { - SetSemanticFunction(&RiscVILhu); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVILhu); SetChildInstruction(); SetChildSemanticFunction(&RiscVZfhFlhChild); @@ -557,7 +731,7 @@ // Test the FP16 load instruction. When looking at the register contents as a // float, it should be NaN. TEST_F(RV32ZfhInstructionTest, RiscVFlh_float_nanbox) { - SetSemanticFunction(&RiscVILhu); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVILhu); SetChildInstruction(); SetChildSemanticFunction(&RiscVZfhFlhChild); @@ -571,7 +745,7 @@ // Test the FP16 load instruction. When looking at the register contents as a // double, it should be NaN. TEST_F(RV32ZfhInstructionTest, RiscVFlh_double_nanbox) { - SetSemanticFunction(&RiscVILhu); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVILhu); SetChildInstruction(); SetChildSemanticFunction(&RiscVZfhFlhChild); @@ -586,14 +760,7 @@ // encoding is preserved in the integer register. TEST_F(RV32ZfhInstructionTest, 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)); - // Fill the upper XLEN-16 bits with the sign bit as per the spec. - uint32_t result = sign ? 0xFFFF'0000 : 0; - result |= static_cast<uint32_t>(half_fp.value); - return result; - }); + FmvXhHelper<RV32Register>(); } // Move half precision from an integer register (lower 16 bits) to a float @@ -610,14 +777,14 @@ // that the destination value is NaN boxed. TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvhx_float_nanbox) { SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvhx); - FmvHxHelper<RV32Register, float>(); + FmvHxNanBoxHelper<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>(); + FmvHxNanBoxHelper<RV32Register, double>(); } // Half precision to single precision conversion. @@ -1157,182 +1324,49 @@ // Test conversion from signed 32 bit integer to half precision. TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtHw) { SetSemanticFunction(&RiscVZfhCvtHw); - UnaryOpWithFflagsMixedTestHelper<RVFpRegister, RV32Register, HalfFP, int32_t>( - "fcvt.h.w", instruction_, {"x", "f"}, 32, - [](int32_t input_int, int rm) -> std::tuple<HalfFP, uint32_t> { - uint32_t fflags = 0; - HalfFP result = FpConversionsTestHelper(static_cast<double>(input_int)) - .ConvertWithFlags<HalfFP>( - fflags, static_cast<FPRoundingMode>(rm)); - return std::make_tuple(result, fflags); - }); -} - -// Test conversion from half precision to signed 32 bit integer. -TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtWh) { - SetSemanticFunction(&RiscVZfhCvtWh); - UnaryOpWithFflagsMixedTestHelper<RV32Register, RVFpRegister, int32_t, HalfFP>( - "fcvt.w.h", instruction_, {"f", "x"}, 32, - [this](HalfFP input, int rm) -> std::tuple<int32_t, uint32_t> { - uint32_t fflags = 0; - double input_double = - FpConversionsTestHelper(input).ConvertWithFlags<double>(fflags); - const int32_t val = - RoundToInteger<double, int32_t>(input_double, rm, fflags); - return std::make_tuple(val, fflags); - }); + CvtHwHelper<RV32Register, int32_t>("fcvt.h.w"); } // Test conversion from unsigned 32 bit integer to half precision. TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtHwu) { SetSemanticFunction(&RiscVZfhCvtHwu); - UnaryOpWithFflagsMixedTestHelper<RVFpRegister, RV32Register, HalfFP, - uint32_t>( - "fcvt.h.wu", instruction_, {"x", "f"}, 32, - [](uint32_t input_int, int rm) -> std::tuple<HalfFP, uint32_t> { - uint32_t fflags = 0; - HalfFP result = FpConversionsTestHelper(static_cast<double>(input_int)) - .ConvertWithFlags<HalfFP>( - fflags, static_cast<FPRoundingMode>(rm)); - return std::make_tuple(result, fflags); - }); + CvtHwHelper<RV32Register, uint32_t>("fcvt.h.wu"); +} + +// Test conversion from half precision to signed 32 bit integer. +TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtWh) { + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhCvtWh); + CvtWhHelper<RV32Register, int32_t>("fcvt.w.h"); } // Test conversion from half precision to unsigned 32 bit integer. TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtWuh) { - SetSemanticFunction(&RiscVZfhCvtWuh); - UnaryOpWithFflagsMixedTestHelper<RV32Register, RVFpRegister, uint32_t, - HalfFP>( - "fcvt.wu.h", instruction_, {"f", "x"}, 32, - [this](HalfFP input, int rm) -> std::tuple<uint32_t, uint32_t> { - uint32_t fflags = 0; - double input_double = - FpConversionsTestHelper(input).ConvertWithFlags<double>(fflags); - const uint32_t val = - RoundToInteger<double, uint32_t>(input_double, rm, fflags); - return std::make_tuple(val, fflags); - }); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhCvtWuh); + CvtWhHelper<RV32Register, uint32_t>("fcvt.wu.h"); } // Test equality comparison for half precision values. TEST_F(RV32ZfhInstructionTest, RiscVZfhFcmpeq) { - SetSemanticFunction(&RiscVZfhFcmpeq); - BinaryOpWithFflagsFPTestHelper<uint64_t, HalfFP, HalfFP>( - "feq.h", instruction_, {"f", "f", "x"}, 32, - [](HalfFP a, HalfFP b) -> std::tuple<uint64_t, uint32_t> { - uint32_t fflags = 0; - double a_f = - FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags); - double b_f = - FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags); - uint64_t result = a_f == b_f ? 1 : 0; - if (std::isnan(a_f) || std::isnan(b_f)) { - result = 0; - } - return std::make_tuple(result, fflags); - }); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFcmpeq); + CmpEqHelper<RV32Register>(); } // Test less than comparison for half precision values. TEST_F(RV32ZfhInstructionTest, RiscVZfhFcmplt) { - SetSemanticFunction(&RiscVZfhFcmplt); - BinaryOpWithFflagsFPTestHelper<uint64_t, HalfFP, HalfFP>( - "flt.h", instruction_, {"f", "f", "x"}, 32, - [](HalfFP a, HalfFP b) -> std::tuple<uint64_t, uint32_t> { - uint32_t fflags = 0; - double a_f = - FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags); - double b_f = - FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags); - uint64_t result = a_f < b_f ? 1 : 0; - if (std::isnan(a_f) || std::isnan(b_f)) { - result = 0; - // LT is a signaling comparison, so the invalid operation flag is - // set. - fflags |= static_cast<uint32_t>( - mpact::sim::riscv::FPExceptions::kInvalidOp); - } - return std::make_tuple(result, fflags); - }); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFcmplt); + CmpLtHelper<RV32Register>(); } // Test less than or equal to comparison for half precision values. TEST_F(RV32ZfhInstructionTest, RiscVZfhFcmple) { - SetSemanticFunction(&RiscVZfhFcmple); - BinaryOpWithFflagsFPTestHelper<uint64_t, HalfFP, HalfFP>( - "fle.h", instruction_, {"f", "f", "x"}, 32, - [](HalfFP a, HalfFP b) -> std::tuple<uint64_t, uint32_t> { - uint32_t fflags = 0; - double a_f = - FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags); - double b_f = - FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags); - uint64_t result = a_f <= b_f ? 1 : 0; - if (std::isnan(a_f) || std::isnan(b_f)) { - result = 0; - // LE is a signaling comparison, so the invalid operation flag is - // set. - fflags |= static_cast<uint32_t>( - mpact::sim::riscv::FPExceptions::kInvalidOp); - } - return std::make_tuple(result, fflags); - }); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFcmple); + CmpLeHelper<RV32Register>(); } // Test classification of half precision values. TEST_F(RV32ZfhInstructionTest, RiscVZfhFclass) { - SetSemanticFunction(&RiscVZfhFclass); - UnaryOpWithFflagsMixedTestHelper<RV32Register, RVFpRegister, uint32_t, - HalfFP>( - "fclass.h", instruction_, {"f", "x"}, 32, - [](HalfFP input, int rm) -> std::tuple<uint32_t, uint32_t> { - uint16_t sign_mask = - ~(FPTypeInfo<HalfFP>::kExpMask | FPTypeInfo<HalfFP>::kSigMask); - uint16_t sign = input.value & sign_mask; - int shift = -1; - - switch (input.value & FPTypeInfo<HalfFP>::kExpMask) { - case 0: - if (input.value & FPTypeInfo<HalfFP>::kSigMask) { - if (sign) { - shift = 2; // Negative subnormal - } else { - shift = 5; // Positive subnormal - } - } else { - if (sign) { - shift = 3; // Negative zero - } else { - shift = 4; // Positive zero - } - } - break; - case FPTypeInfo<HalfFP>::kExpMask: - if (input.value & FPTypeInfo<HalfFP>::kSigMask) { - if (FPTypeInfo<HalfFP>::IsQNaN(input)) { - shift = 9; // Quiet NaN - } else { - shift = 8; // Signaling NaN - } - } else { // Inf - if (sign) { - shift = 0; // Negative infinity - } else { - shift = 7; // Positive infinity - } - } - break; - default: - if (sign) { - shift = 1; // Negative normal - } else { - shift = 6; // Positive normal - } - break; - } - EXPECT_GE(shift, 0) << "The test didn't set the expected result."; - return std::make_tuple(1 << shift, 0); - }); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFclass); + ClassHelper<RV32Register>(); } // Test fused multiply add for half precision values. @@ -1481,14 +1515,7 @@ // encoding is preserved in the integer register. TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvxh) { SetSemanticFunction(&mpact::sim::riscv::RV64::RiscVZfhFMvxh); - UnaryOpFPTestHelper<uint64_t, HalfFP>( - "fmv.x.h", instruction_, {"f", "x"}, 32, [](HalfFP half_fp) -> uint64_t { - bool sign = 1 & (half_fp.value >> (FPTypeInfo<HalfFP>::kBitSize - 1)); - // Fill the upper XLEN-16 bits with the sign bit as per the spec. - uint64_t result = sign ? 0xFFFF'FFFF'FFFF'0000 : 0; - result |= static_cast<uint64_t>(half_fp.value); - return result; - }); + FmvXhHelper<RV64Register>(); } // Move half precision from an integer register (lower 16 bits) to a float @@ -1505,20 +1532,20 @@ // that the destination value is NaN boxed. TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvhx_float_nanbox) { SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFMvhx); - FmvHxHelper<RV64Register, float>(); + FmvHxNanBoxHelper<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>(); + FmvHxNanBoxHelper<RV64Register, double>(); } // Test the FP16 load instruction. The semantic functions should match the isa // file. TEST_F(RV64ZfhInstructionTest, RiscVFlh) { - SetSemanticFunction(&RiscVILhu); + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVILhu); SetChildInstruction(); SetChildSemanticFunction(&RiscVZfhFlhChild); @@ -1532,7 +1559,7 @@ // Test the FP16 load instruction. When looking at the register contents as a // float, it should be NaN. TEST_F(RV64ZfhInstructionTest, RiscVFlh_float_nanbox) { - SetSemanticFunction(&RiscVILhu); + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVILhu); SetChildInstruction(); SetChildSemanticFunction(&RiscVZfhFlhChild); @@ -1546,7 +1573,7 @@ // Test the FP16 load instruction. When looking at the register contents as a // double, it should be NaN. TEST_F(RV64ZfhInstructionTest, RiscVFlh_double_nanbox) { - SetSemanticFunction(&RiscVILhu); + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVILhu); SetChildInstruction(); SetChildSemanticFunction(&RiscVZfhFlhChild); @@ -1557,4 +1584,52 @@ EXPECT_TRUE(std::isnan(observed_val)); } +// Test conversion from signed 32 bit integer to half precision. +TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtHw) { + SetSemanticFunction(&RiscVZfhCvtHw); + CvtHwHelper<RV64Register, int32_t>("fcvt.h.w"); +} + +// Test conversion from unsigned 32 bit integer to half precision. +TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtHwu) { + SetSemanticFunction(&RiscVZfhCvtHwu); + CvtHwHelper<RV64Register, uint32_t>("fcvt.h.wu"); +} + +// Test conversion from half precision to signed 32 bit integer. +TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtWh) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhCvtWh); + CvtWhHelper<RV64Register, int32_t>("fcvt.w.h"); +} + +// Test conversion from half precision to unsigned 32 bit integer. +TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtWuh) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhCvtWuh); + CvtWhHelper<RV64Register, uint32_t>("fcvt.wu.h"); +} + +// Test equality comparison for half precision values. +TEST_F(RV64ZfhInstructionTest, RiscVZfhFcmpeq) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFcmpeq); + CmpEqHelper<RV64Register>(); +} + +// Test less than comparison for half precision values. +TEST_F(RV64ZfhInstructionTest, RiscVZfhFcmplt) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFcmplt); + CmpLtHelper<RV64Register>(); +} + +// Test less than or equal to comparison for half precision values. +TEST_F(RV64ZfhInstructionTest, RiscVZfhFcmple) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFcmple); + CmpLeHelper<RV64Register>(); +} + +// Test classification of half precision values. +TEST_F(RV64ZfhInstructionTest, RiscVZfhFclass) { + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFclass); + ClassHelper<RV64Register>(); +} + } // namespace