zfh semantic functions: Add conversions between 64bit integers and half precision
* fcvt.l.h
* fcvt.lu.h
* fcvt.h.l
* fcvt.h.lu

PiperOrigin-RevId: 761730156
Change-Id: I1594e48a32ca99dab3d2ff9ff1d6e66f2a500c38
diff --git a/riscv/riscv_zfh.isa b/riscv/riscv_zfh.isa
index 76265d5..66c944d 100644
--- a/riscv/riscv_zfh.isa
+++ b/riscv/riscv_zfh.isa
@@ -262,5 +262,21 @@
       resources: { next_pc, frs1 : rd[0..]},
       semfunc: "&RV64::RiscVZfhFclass",
       disasm: "fclass.h", "%rd, %frs1";
+    fcvt_lh{: frs1, rm : rd, fflags},
+      resources: {next_pc, frs1 : rd[0..]},
+      semfunc: "&RV64::RiscVZfhCvtLh",
+      disasm: "fcvt.l.h", "%rd, %frs1";
+    fcvt_luh{: frs1, rm : rd, fflags},
+      resources: {next_pc, frs1 : rd[0..]},
+      semfunc: "&RV64::RiscVZfhCvtLuh",
+      disasm: "fcvt.lu.h", "%rd, %frs1";
+    fcvt_hl{: rs1, rm : frd, fflags},
+      resources: {next_pc, frs1 : frd[0..]},
+      semfunc: "&RV64::RiscVZfhCvtHl",
+      disasm: "fcvt.h.l", "%frd, %rs1";
+    fcvt_hlu{: rs1, rm : frd, fflags},
+      resources: {next_pc, frs1 : frd[0..]},
+      semfunc: "&RV64::RiscVZfhCvtHlu",
+      disasm: "fcvt.h.lu", "%frd, %rs1";
   }
 }
diff --git a/riscv/riscv_zfh_instructions.cc b/riscv/riscv_zfh_instructions.cc
index 23af707..e807747 100644
--- a/riscv/riscv_zfh_instructions.cc
+++ b/riscv/riscv_zfh_instructions.cc
@@ -705,6 +705,36 @@
       });
 }
 
+// Converts from half precision to signed 64 bit integer.
+void RiscVZfhCvtLh(const Instruction *instruction) {
+  RiscVConvertFloatWithFflagsOp<typename RV64Register::ValueType, HalfFP,
+                                int64_t>(instruction);
+}
+
+// Converts from half precision to unsigned 64 bit integer.
+void RiscVZfhCvtLuh(const Instruction *instruction) {
+  RiscVConvertFloatWithFflagsOp<typename RV64Register::ValueType, HalfFP,
+                                uint64_t>(instruction);
+}
+
+// Converts from signed 64 bit integer to half precision.
+void RiscVZfhCvtHl(const Instruction *instruction) {
+  RiscVZfhCvtHelper<HalfFP, int64_t>(
+      instruction,
+      [](int64_t a, FPRoundingMode rm, uint32_t &fflags) -> HalfFP {
+        return ConvertToHalfFP(static_cast<float>(a), rm, fflags);
+      });
+}
+
+// Convert from unsigned 64 bit integer to half precision.
+void RiscVZfhCvtHlu(const Instruction *instruction) {
+  RiscVZfhCvtHelper<HalfFP, uint64_t>(
+      instruction,
+      [](uint64_t a, FPRoundingMode rm, uint32_t &fflags) -> HalfFP {
+        return ConvertToHalfFP(static_cast<float>(a), rm, fflags);
+      });
+}
+
 }  // namespace RV64
 
 void RiscVZfhFlhChild(const Instruction *instruction) {
diff --git a/riscv/riscv_zfh_instructions.h b/riscv/riscv_zfh_instructions.h
index f4e67c8..576d7f2 100644
--- a/riscv/riscv_zfh_instructions.h
+++ b/riscv/riscv_zfh_instructions.h
@@ -145,6 +145,38 @@
 //   rd: Integer Register
 void RiscVZfhFclass(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 RiscVZfhCvtLh(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 RiscVZfhCvtLuh(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 RiscVZfhCvtHl(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 RiscVZfhCvtHlu(const Instruction *instruction);
+
 }  // namespace RV64
 
 // Source Operands: *none*
diff --git a/riscv/test/riscv_zfh_instructions_test.cc b/riscv/test/riscv_zfh_instructions_test.cc
index bf8365a..e5e8d73 100644
--- a/riscv/test/riscv_zfh_instructions_test.cc
+++ b/riscv/test/riscv_zfh_instructions_test.cc
@@ -84,6 +84,10 @@
 using ::mpact::sim::riscv::RVFpRegister;
 using ::mpact::sim::riscv::ScopedFPRoundingMode;
 using ::mpact::sim::riscv::ScopedFPStatus;
+using ::mpact::sim::riscv::RV64::RiscVZfhCvtHl;
+using ::mpact::sim::riscv::RV64::RiscVZfhCvtHlu;
+using ::mpact::sim::riscv::RV64::RiscVZfhCvtLh;
+using ::mpact::sim::riscv::RV64::RiscVZfhCvtLuh;
 
 using ::mpact::sim::riscv::test::FloatingPointToString;
 using ::mpact::sim::riscv::test::FPCompare;
@@ -242,6 +246,7 @@
 }
 
 // Helper for unary instructions that go between floats and integers.
+// TODO(b/419352093): Change the rounding mode datatype to int.
 template <typename XRegister>
 template <typename DestRegisterType, typename LhsRegisterType, typename R,
           typename LHS>
@@ -1652,4 +1657,64 @@
   ClassHelper();
 }
 
+// Test conversion from half precision to signed 64 bit integer.
+TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtLh) {
+  SetSemanticFunction(&RiscVZfhCvtLh);
+  UnaryOpWithFflagsMixedTestHelper<RV64Register, RVFpRegister, int64_t, HalfFP>(
+      "fcvt.l.h", instruction_, {"f", "x"}, 32,
+      [this](HalfFP input, uint32_t rm) -> std::tuple<int64_t, uint32_t> {
+        uint32_t fflags = 0;
+        double input_double =
+            FpConversionsTestHelper(input).ConvertWithFlags<double>(fflags);
+        int64_t val =
+            this->RoundToInteger<double, int64_t>(input_double, rm, fflags);
+        return std::tuple(val, fflags);
+      });
+}
+
+// Test conversion from half precision to unsigned 64 bit integer.
+TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtLuh) {
+  SetSemanticFunction(&RiscVZfhCvtLuh);
+  UnaryOpWithFflagsMixedTestHelper<RV64Register, RVFpRegister, uint64_t,
+                                   HalfFP>(
+      "fcvt.lu.h", instruction_, {"f", "x"}, 32,
+      [this](HalfFP input, uint32_t rm) -> std::tuple<uint64_t, uint32_t> {
+        uint32_t fflags = 0;
+        double input_double =
+            FpConversionsTestHelper(input).ConvertWithFlags<double>(fflags);
+        uint64_t val =
+            this->RoundToInteger<double, uint64_t>(input_double, rm, fflags);
+        return std::tuple(val, fflags);
+      });
+}
+
+// Test conversion from signed 64 bit integer to half precision.
+TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtHl) {
+  SetSemanticFunction(&RiscVZfhCvtHl);
+  UnaryOpWithFflagsMixedTestHelper<RVFpRegister, RV64Register, HalfFP, int64_t>(
+      "fcvt.h.l", instruction_, {"x", "f"}, 32,
+      [](int64_t input_int, uint32_t 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::tuple(result, fflags);
+      });
+}
+
+// Test conversion from unsigned 64 bit integer to half precision.
+TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtHlu) {
+  SetSemanticFunction(&RiscVZfhCvtHlu);
+  UnaryOpWithFflagsMixedTestHelper<RVFpRegister, RV64Register, HalfFP,
+                                   uint64_t>(
+      "fcvt.h.lu", instruction_, {"x", "f"}, 32,
+      [](uint64_t input_int, uint32_t 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::tuple(result, fflags);
+      });
+}
+
 }  // namespace