No public description

PiperOrigin-RevId: 641011171
Change-Id: If0c4721ea3c770021f1d7e76d849b084cd501351
diff --git a/cheriot/BUILD b/cheriot/BUILD
index 7d7e169..45446af 100644
--- a/cheriot/BUILD
+++ b/cheriot/BUILD
@@ -54,8 +54,6 @@
         "cheriot_register.cc",
         "cheriot_state.cc",
         "riscv_cheriot_a_instructions.cc",
-        "riscv_cheriot_f_instructions.cc",
-        "riscv_cheriot_fp_state.cc",
         "riscv_cheriot_i_instructions.cc",
         "riscv_cheriot_instructions.cc",
         "riscv_cheriot_m_instructions.cc",
@@ -68,8 +66,6 @@
         "cheriot_state.h",
         "riscv_cheriot_a_instructions.h",
         "riscv_cheriot_csr_enum.h",
-        "riscv_cheriot_f_instructions.h",
-        "riscv_cheriot_fp_state.h",
         "riscv_cheriot_i_instructions.h",
         "riscv_cheriot_instruction_helpers.h",
         "riscv_cheriot_instructions.h",
@@ -78,10 +74,6 @@
         "riscv_cheriot_priv_instructions.h",
         "riscv_cheriot_zicsr_instructions.h",
     ],
-    copts = [
-        "-ffp-model=strict",
-        "-fprotect-parens",
-    ],
     tags = ["not_run:arm"],
     deps = [
         "@com_google_absl//absl/container:flat_hash_map",
@@ -317,20 +309,20 @@
     ],
 )
 
-cc_library(
-    name = "cheriot_renode",
+cc_binary(
+    name = "renode_mpact_cheriot",
     srcs = [
         "cheriot_cli_forwarder.cc",
-        "cheriot_renode.cc",
-        "cheriot_renode_cli_top.cc",
-        "cheriot_renode_register_info.cc",
-    ],
-    hdrs = [
         "cheriot_cli_forwarder.h",
+        "cheriot_renode.cc",
         "cheriot_renode.h",
+        "cheriot_renode_cli_top.cc",
         "cheriot_renode_cli_top.h",
+        "cheriot_renode_register_info.cc",
         "cheriot_renode_register_info.h",
     ],
+    linkshared = True,
+    linkstatic = True,
     deps = [
         ":cheriot_debug_info",
         ":cheriot_debug_interface",
@@ -362,16 +354,6 @@
     ],
 )
 
-cc_binary(
-    name = "renode_mpact_cheriot",
-    linkshared = True,
-    linkstatic = True,
-    deps = [
-        ":cheriot_renode",
-        "@com_google_mpact-sim//mpact/sim/util/renode",
-    ],
-)
-
 cc_library(
     name = "cheriot_test_rig_lib",
     srcs = [
diff --git a/cheriot/cheriot_state.h b/cheriot/cheriot_state.h
index 42d22c5..7df9bba 100644
--- a/cheriot/cheriot_state.h
+++ b/cheriot/cheriot_state.h
@@ -69,7 +69,6 @@
 
 // Forward declare the CHERIoT register type.
 class CheriotRegister;
-class RiscVCheriotFPState;
 
 // CHERIoT exception codes. These are used in addition to the ones defined for
 // vanilla RiscV.
@@ -308,9 +307,6 @@
   const CheriotRegister *executable_root() const { return executable_root_; }
   const CheriotRegister *sealing_root() const { return sealing_root_; }
   const CheriotRegister *memory_root() const { return memory_root_; }
-  // Floating point state.
-  RiscVCheriotFPState *rv_fp() const { return rv_fp_; }
-  void set_rv_fp(RiscVCheriotFPState *value) { rv_fp_ = value; }
   // Special capability registers. Pcc replaces the pc. Cgp is a global pointer
   // capability that is aliased with c3.
   CheriotRegister *pcc() const { return pcc_; }
@@ -390,7 +386,6 @@
   bool branch_ = false;
   uint64_t max_physical_address_;
   uint64_t min_physical_address_ = 0;
-  RiscVCheriotFPState *rv_fp_ = nullptr;
   int num_tags_per_load_;
   util::TaggedMemoryInterface *owned_tagged_memory_ = nullptr;
   util::TaggedMemoryInterface *tagged_memory_;
diff --git a/cheriot/cheriot_test_rig.cc b/cheriot/cheriot_test_rig.cc
index 0daca50..ae9d3a7 100644
--- a/cheriot/cheriot_test_rig.cc
+++ b/cheriot/cheriot_test_rig.cc
@@ -60,8 +60,6 @@
       absl::bind_front(&CheriotTestRig::OnStore, this)));
   // Set up sim state.
   state_ = new CheriotState(kCheriotTestRigName, tagged_memory_watcher_);
-  fp_state_ = new RiscVCheriotFPState(state_);
-  state_->set_rv_fp(fp_state_);
   // Initialize pcc to 0x8000'0000.
   pcc_ = static_cast<CheriotRegister *>(
       state_->registers()->at(CheriotState::kPcName));
@@ -123,7 +121,6 @@
 CheriotTestRig::~CheriotTestRig() {
   delete cheriot_decoder_;
   delete state_;
-  delete fp_state_;
   delete tagged_memory_;
   delete tagged_memory_watcher_;
   // Deallocate data buffers.
diff --git a/cheriot/cheriot_test_rig.h b/cheriot/cheriot_test_rig.h
index 48f04d3..f8f714c 100644
--- a/cheriot/cheriot_test_rig.h
+++ b/cheriot/cheriot_test_rig.h
@@ -23,7 +23,6 @@
 #include "cheriot/cheriot_register.h"
 #include "cheriot/cheriot_state.h"
 #include "cheriot/cheriot_test_rig_decoder.h"
-#include "cheriot/riscv_cheriot_fp_state.h"
 #include "cheriot/test_rig_packets.h"
 #include "mpact/sim/generic/component.h"
 #include "mpact/sim/generic/counters.h"
@@ -73,7 +72,6 @@
 
   int trace_version_ = 1;
   CheriotState *state_;
-  RiscVCheriotFPState *fp_state_;
   CheriotRegister *pcc_;
   CheriotTestRigDecoder *cheriot_decoder_ = nullptr;
   util::TaggedMemoryInterface *tagged_memory_ = nullptr;
diff --git a/cheriot/cheriot_top.cc b/cheriot/cheriot_top.cc
index ad8574d..a8b7221 100644
--- a/cheriot/cheriot_top.cc
+++ b/cheriot/cheriot_top.cc
@@ -35,7 +35,6 @@
 #include "cheriot/cheriot_register.h"
 #include "cheriot/cheriot_state.h"
 #include "cheriot/riscv_cheriot_enums.h"
-#include "cheriot/riscv_cheriot_fp_state.h"
 #include "cheriot/riscv_cheriot_register_aliases.h"
 #include "mpact/sim/generic/component.h"
 #include "mpact/sim/generic/core_debug_interface.h"
@@ -151,7 +150,6 @@
   delete cheriot_decode_cache_;
   delete cheriot_decoder_;
   delete state_;
-  delete fp_state_;
   delete tagged_watcher_;
   delete atomic_watcher_;
   delete atomic_memory_;
@@ -164,8 +162,6 @@
   atomic_watcher_ = new util::MemoryWatcher(atomic_memory_if_);
   atomic_memory_ = new util::AtomicMemory(atomic_watcher_);
   state_ = new CheriotState(kCheriotName, tagged_watcher_, atomic_memory_);
-  fp_state_ = new RiscVCheriotFPState(state_);
-  state_->set_rv_fp(fp_state_);
   pcc_ = static_cast<CheriotRegister *>(
       state_->registers()->at(CheriotState::kPcName));
   // Set up the decoder and decode cache.
diff --git a/cheriot/cheriot_top.h b/cheriot/cheriot_top.h
index e07838f..98d0421 100644
--- a/cheriot/cheriot_top.h
+++ b/cheriot/cheriot_top.h
@@ -30,7 +30,6 @@
 #include "cheriot/cheriot_register.h"
 #include "cheriot/cheriot_state.h"
 #include "cheriot/riscv_cheriot_enums.h"
-#include "cheriot/riscv_cheriot_fp_state.h"
 #include "mpact/sim/generic/component.h"
 #include "mpact/sim/generic/core_debug_interface.h"
 #include "mpact/sim/generic/counters.h"
@@ -179,7 +178,6 @@
   absl::Notification *run_halted_ = nullptr;
   // The local CherIoT state.
   CheriotState *state_;
-  RiscVCheriotFPState *fp_state_;
   // Flag that indicates an instruction needs to be stepped over.
   bool need_to_step_over_ = false;
   // Action point manager.
diff --git a/cheriot/riscv_cheriot_fp_state.cc b/cheriot/riscv_cheriot_fp_state.cc
deleted file mode 100644
index 1dcbf52..0000000
--- a/cheriot/riscv_cheriot_fp_state.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2024 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cheriot/riscv_cheriot_fp_state.h"
-
-#include <cstdint>
-
-#include "absl/log/log.h"
-#include "cheriot/cheriot_state.h"
-#include "mpact/sim/generic/type_helpers.h"
-#include "riscv//riscv_csr.h"
-#include "riscv//riscv_fp_host.h"
-#include "riscv//riscv_fp_info.h"
-
-namespace mpact {
-namespace sim {
-namespace cheriot {
-
-using ::mpact::sim::riscv::FPRoundingMode;
-using ::mpact::sim::generic::operator*;  // NOLINT: is used below (clang error).
-using ::mpact::sim::riscv::GetHostFloatingPointInterface;
-using ::mpact::sim::riscv::RiscVCsrEnum;
-
-constexpr char kFcsrName[] = "fcsr";
-constexpr uint32_t kFcsrInitial = 0b000'0'0'0'0'0;
-constexpr uint32_t kFcsrReadMask = 0b111'1'1111;
-constexpr uint32_t kFcsrWriteMask = 0b111'1'1111;
-
-constexpr char kFflagsName[] = "fflags";
-constexpr uint32_t kFflagsInitial = 0b0'0000;
-constexpr uint32_t kFflagsReadMask = 0b1'1111;
-constexpr uint32_t kFflagsWriteMask = 0b1'1111;
-
-constexpr char kFrmName[] = "frm";
-constexpr uint32_t kFrmInitial = 0b000;
-constexpr uint32_t kFrmReadMask = 0b111;
-constexpr uint32_t kFrmWriteMask = 0b111;
-
-// Helper function to avoid some extra code below.
-static inline void LogIfError(absl::Status status) {
-  if (status.ok()) return;
-  LOG(ERROR) << status.message();
-}
-
-RiscVCheriotFPState::RiscVCheriotFPState(CheriotState *rv_state)
-    : rv_state_(rv_state) {
-  fcsr_ = new RiscVFcsr(this);
-  frm_ = new RiscVFrm(this);
-  fflags_ = new RiscVFflags(this);
-  host_fp_interface_ = GetHostFloatingPointInterface();
-
-  LogIfError(rv_state_->csr_set()->AddCsr(fcsr_));
-  LogIfError(rv_state_->csr_set()->AddCsr(frm_));
-  LogIfError(rv_state_->csr_set()->AddCsr(fflags_));
-}
-
-RiscVCheriotFPState::~RiscVCheriotFPState() {
-  delete fcsr_;
-  delete frm_;
-  delete fflags_;
-  delete host_fp_interface_;
-}
-
-FPRoundingMode RiscVCheriotFPState::GetRoundingMode() const {
-  return rounding_mode_;
-}
-
-void RiscVCheriotFPState::SetRoundingMode(FPRoundingMode mode) {
-  if (rounding_mode_ != mode) {
-    switch (mode) {
-      case FPRoundingMode::kRoundToNearestTiesToMax:
-      case FPRoundingMode::kRoundToNearest:
-        break;
-      case FPRoundingMode::kRoundTowardsZero:
-        break;
-      case FPRoundingMode::kRoundDown:
-        break;
-      case FPRoundingMode::kRoundUp:
-        break;
-      default:
-        rounding_mode_valid_ = false;
-        LOG(ERROR) << "Illegal rounding mode: " << static_cast<int>(mode);
-        return;
-    }
-    rounding_mode_ = mode;
-    frm_->Write(*mode);
-    rounding_mode_valid_ = true;
-  }
-}
-
-// The RiscV fp csr.
-RiscVFcsr::RiscVFcsr(RiscVCheriotFPState *fp_state)
-    : RiscVSimpleCsr<uint32_t>(kFcsrName, RiscVCsrEnum::kFCsr, kFcsrInitial,
-                               kFcsrReadMask, kFcsrWriteMask,
-                               fp_state->rv_state()),
-      fp_state_(fp_state) {}
-
-// The status value is computed from the most recent x86 status value.
-uint32_t RiscVFcsr::AsUint32() {
-  uint32_t status_value = fp_state_->host_fp_interface()->GetRiscVFcsr();
-  auto csr_value = GetUint32();
-  auto value = ((csr_value & ~0x1f) | (status_value & 0x1f)) & read_mask();
-  return value;
-}
-
-uint64_t RiscVFcsr::AsUint64() { return AsUint32(); }
-
-// The status value is converted to x86 and stored for use in next fp
-// instruction.
-void RiscVFcsr::Write(uint32_t value) {
-  auto wr_value = value & write_mask();
-  Set(wr_value);
-  fp_state_->host_fp_interface()->SetRiscVFcsr(wr_value);
-  fp_state_->SetRoundingMode(
-      static_cast<FPRoundingMode>((wr_value >> 5) & 0b111));
-}
-
-void RiscVFcsr::Write(uint64_t value) { Write(static_cast<uint32_t>(value)); }
-
-// RiscVFflags translates reads and writes into reads and writes of fcsr.
-RiscVFflags::RiscVFflags(RiscVCheriotFPState *fp_state)
-    : RiscVSimpleCsr<uint32_t>(kFflagsName, RiscVCsrEnum::kFFlags,
-                               kFflagsInitial, kFflagsReadMask,
-                               kFflagsWriteMask, fp_state->rv_state()),
-      fp_state_(fp_state) {}
-
-uint32_t RiscVFflags::AsUint32() {
-  uint32_t value = fp_state_->fcsr()->AsUint32();
-  value &= 0b1'1111;
-  return value;
-}
-
-void RiscVFflags::Write(uint32_t value) {
-  uint32_t current = fp_state_->fcsr()->AsUint32();
-  uint32_t new_value = (current & ~write_mask()) | (value & write_mask());
-  if (new_value == current) return;
-  fp_state_->fcsr()->Write(new_value);
-}
-
-uint32_t RiscVFflags::GetUint32() { return AsUint32(); }
-
-void RiscVFflags::Set(uint32_t value) { Write(value); }
-
-// RiscV rm (rounding mode) csr translates reads and writes into reads
-// and writes of fcsr.
-RiscVFrm::RiscVFrm(RiscVCheriotFPState *fp_state)
-    : RiscVSimpleCsr<uint32_t>(kFrmName, RiscVCsrEnum::kFrm, kFrmInitial,
-                               kFrmReadMask, kFrmWriteMask,
-                               fp_state->rv_state()),
-      fp_state_(fp_state) {}
-
-uint32_t RiscVFrm::AsUint32() {
-  uint32_t value = fp_state_->fcsr()->AsUint32();
-  uint32_t rm = (value >> 5) & read_mask();
-  return rm;
-}
-
-void RiscVFrm::Write(uint32_t value) {
-  uint32_t wr_value = value & write_mask();
-  uint32_t fcsr = fp_state_->fcsr()->AsUint32();
-  uint32_t new_fcsr = ((fcsr & 0b1'1111) | (wr_value << 5));
-  fp_state_->fcsr()->Write(new_fcsr);
-}
-
-uint32_t RiscVFrm::GetUint32() { return AsUint32(); }
-
-void RiscVFrm::Set(uint32_t value) { Write(value); }
-
-}  // namespace cheriot
-}  // namespace sim
-}  // namespace mpact
diff --git a/cheriot/riscv_cheriot_instruction_helpers.h b/cheriot/riscv_cheriot_instruction_helpers.h
index 74b6e44..ee5af8d 100644
--- a/cheriot/riscv_cheriot_instruction_helpers.h
+++ b/cheriot/riscv_cheriot_instruction_helpers.h
@@ -27,15 +27,12 @@
 #include "absl/log/log.h"
 #include "cheriot/cheriot_register.h"
 #include "cheriot/cheriot_state.h"
-#include "cheriot/riscv_cheriot_fp_state.h"
 #include "mpact/sim/generic/arch_state.h"
 #include "mpact/sim/generic/data_buffer.h"
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/generic/operand_interface.h"
 #include "mpact/sim/generic/register.h"
 #include "mpact/sim/generic/type_helpers.h"
-#include "riscv//riscv_fp_host.h"
-#include "riscv//riscv_fp_info.h"
 #include "riscv//riscv_state.h"
 
 namespace mpact {
@@ -44,12 +41,7 @@
 
 using ::mpact::sim::generic::Instruction;
 using ::mpact::sim::generic::operator*;
-using ::mpact::sim::generic::FPTypeInfo;
 using ::mpact::sim::generic::RegisterBase;
-using ::mpact::sim::riscv::FPExceptions;
-using ::mpact::sim::riscv::FPRoundingMode;
-using ::mpact::sim::riscv::ScopedFPRoundingMode;
-using ::mpact::sim::riscv::ScopedFPStatus;
 using CapReg = CheriotRegister;
 using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits;
 
@@ -70,157 +62,6 @@
   cap_reg->set_is_null();
 }
 
-// Templated helper function for convert instruction semantic functions.
-template <typename From, typename To>
-inline std::tuple<To, uint32_t> CvtHelper(From value) {
-  constexpr From kMax = static_cast<From>(std::numeric_limits<To>::max());
-  constexpr From kMin = static_cast<From>(std::numeric_limits<To>::min());
-
-  if (FPTypeInfo<From>::IsNaN(value)) {
-    return std::make_tuple(std::numeric_limits<To>::max(),
-                           *FPExceptions::kInvalidOp);
-  }
-  if (value > kMax) {
-    return std::make_tuple(std::numeric_limits<To>::max(),
-                           *FPExceptions::kInvalidOp);
-  }
-  if (value < kMin) {
-    if (std::is_unsigned<To>::value && (value > -1.0)) {
-      using SignedTo = typename std::make_signed<To>::type;
-      SignedTo signed_val = static_cast<SignedTo>(value);
-      if (signed_val == 0) {
-        return std::make_tuple(0, *FPExceptions::kInexact);
-      }
-    }
-    return std::make_tuple(std::numeric_limits<To>::min(),
-                           *FPExceptions::kInvalidOp);
-  }
-
-  auto output_value = static_cast<To>(value);
-  return std::make_tuple(output_value, 0);
-}
-
-// TODO(torerik): Modify as needed if used to produce values in capability
-// registers.
-// Generic helper function for floating op instructions that do not
-// require NaN boxing since they produce non fp-values, but set fflags.
-template <typename CapRegType, typename Result, typename From, typename To>
-inline void RiscVConvertFloatWithFflagsOp(const Instruction *instruction) {
-  constexpr From kMax = static_cast<From>(std::numeric_limits<To>::max());
-  constexpr From kMin = static_cast<From>(std::numeric_limits<To>::min());
-
-  From lhs = generic::GetInstructionSource<From>(instruction, 0);
-
-  uint32_t flags = 0;
-  uint32_t rm = generic::GetInstructionSource<uint32_t>(instruction, 1);
-  To value = 0;
-  if (FPTypeInfo<From>::IsNaN(lhs)) {
-    value = std::numeric_limits<To>::max();
-    flags = *FPExceptions::kInvalidOp;
-  } else if (lhs >= kMax) {
-    value = std::numeric_limits<To>::max();
-    flags = *FPExceptions::kInvalidOp;
-  } else if (lhs < kMin) {
-    bool is_set = false;
-    if (std::is_unsigned<To>::value && (lhs > -1.0)) {
-      using SignedTo = typename std::make_signed<To>::type;
-      SignedTo signed_val = static_cast<SignedTo>(lhs);
-      if (signed_val == 0) {
-        value = 0;
-        flags = *FPExceptions::kInexact;
-        is_set = true;
-      }
-    }
-    if (!is_set) {
-      value = std::numeric_limits<To>::min();
-      flags = *FPExceptions::kInvalidOp;
-      is_set = true;
-    }
-  } else if (lhs == 0.0) {
-    value = 0;
-  } else {
-    // static_cast<>() doesn't necessarily round, so will have to force
-    // rounding before converting to the integer type if necessary.
-    using FromUint = typename FPTypeInfo<From>::UIntType;
-    auto constexpr kBias = FPTypeInfo<From>::kExpBias;
-    auto constexpr kExpMask = FPTypeInfo<From>::kExpMask;
-    auto constexpr kSigSize = FPTypeInfo<From>::kSigSize;
-    auto constexpr kSigMask = FPTypeInfo<From>::kSigMask;
-    FromUint lhs_u = *reinterpret_cast<FromUint *>(&lhs);
-    FromUint exp = kExpMask & lhs_u;
-    int exp_value = exp >> kSigSize;
-    int unbiased_exp = exp_value - kBias;
-    FromUint sig = kSigMask & lhs_u;
-    // If the number of bits in the significand is greater or equal to
-    // unbiased exponent, and there is a 1 among the extra bits, we need to
-    // perform rounding.
-    if (unbiased_exp < 0) {
-      flags = *FPExceptions::kInexact;
-    } else if (unbiased_exp <= kSigSize) {
-      FromUint mask = (1ULL << (kSigSize - unbiased_exp)) - 1;
-      if ((sig & mask) != 0) {
-        flags = *FPExceptions::kInexact;
-        FromUint sign = lhs_u & (1ULL << (FPTypeInfo<From>::kBitSize - 1));
-        // Turn the value into a denormal.
-        constexpr FromUint hidden_bit = 1ULL << (kSigSize - 1);
-        FromUint tmp_u = sign | hidden_bit | (sig >> 1);
-        From tmp = *reinterpret_cast<From *>(&tmp_u);
-        // Divide so that only the bits we care about are left in the
-        // significand.
-        int shift = kBias + kSigSize - exp_value - 1;
-        FromUint div_exp = shift + kBias;
-        FromUint div_u = div_exp << kSigSize;
-        From div = *reinterpret_cast<From *>(&div_u);
-        auto *rv_fp =
-            static_cast<CheriotState *>(instruction->state())->rv_fp();
-        auto *host_fp_interface = rv_fp->host_fp_interface();
-        {
-          // The rounding happens during this division.
-          ScopedFPRoundingMode set_fp_rm(host_fp_interface, rm);
-          tmp /= div;
-        }
-        // Convert back to normalized number, by using the original sign
-        // and exponent, and the normalized and significand from the division.
-        tmp_u = *reinterpret_cast<FromUint *>(&tmp);
-        lhs_u = sign | exp | ((tmp_u << (shift + 1)) & kSigMask);
-        lhs = *reinterpret_cast<From *>(&lhs_u);
-      }
-    }
-    value = static_cast<To>(lhs);
-  }
-  using SignedTo = typename std::make_signed<To>::type;
-  // The final value is sign-extended to the register width, even if it's
-  // conversion to an unsigned value.
-  SignedTo signed_value = static_cast<SignedTo>(value);
-  Result dest_value = static_cast<Result>(signed_value);
-  WriteCapIntResult(instruction, 0, dest_value);
-  if (flags) {
-    auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
-    flag_db->Set<uint32_t>(0, flags);
-    flag_db->Submit();
-  }
-}
-
-// Helper function to read a NaN boxed source value, converting it to NaN if
-// it isn't formatted properly.
-template <typename RegValue, typename Argument>
-inline Argument GetNaNBoxedSource(const Instruction *instruction, int arg) {
-  if (sizeof(RegValue) <= sizeof(Argument)) {
-    return generic::GetInstructionSource<Argument>(instruction, arg);
-  } else {
-    using SInt = typename std::make_signed<RegValue>::type;
-    using UInt = typename std::make_unsigned<RegValue>::type;
-    SInt val = generic::GetInstructionSource<SInt>(instruction, arg);
-    UInt uval = static_cast<UInt>(val);
-    UInt mask = std::numeric_limits<UInt>::max() << (sizeof(Argument) * 8);
-    if (((mask & uval) != mask)) {
-      return *reinterpret_cast<const Argument *>(
-          &FPTypeInfo<Argument>::kCanonicalNaN);
-    }
-    return generic::GetInstructionSource<Argument>(instruction, arg);
-  }
-}
-
 template <typename Register, typename Result, typename Argument>
 inline void RiscVBinaryOp(const Instruction *instruction,
                           std::function<Result(Argument, Argument)> operation) {
@@ -414,335 +255,6 @@
   db->DecRef();
 }
 
-// Generic helper function for binary instructions that take NaN boxed sources
-// but produce the result in a capability register.
-template <typename RegValue, typename Result, typename Argument>
-inline void RVCheriotBinaryNaNBoxOp(
-    const Instruction *instruction,
-    std::function<Result(Argument, Argument)> operation) {
-  Argument lhs = GetNaNBoxedSource<RegValue, Argument>(instruction, 0);
-  Argument rhs = GetNaNBoxedSource<RegValue, Argument>(instruction, 1);
-  Result dest_value = operation(lhs, rhs);
-  // Check to see if we need to NaN box the result.
-  if (sizeof(RegValue) > sizeof(Result)) {
-    // If the floating point value is narrower than the register, the upper
-    // bits have to be set to all ones.
-    using UReg = typename std::make_unsigned<RegValue>::type;
-    using UInt = typename FPTypeInfo<Result>::UIntType;
-    auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
-    UReg reg_value = std::numeric_limits<UReg>::max();
-    int shift = 8 * (sizeof(RegValue) - sizeof(Result));
-    reg_value = (reg_value << shift) | dest_u_value;
-    WriteCapIntResult(instruction, 0, reg_value);
-    return;
-  }
-  WriteCapIntResult(instruction, 0, dest_value);
-}
-
-// Generic helper function for binary instructions with NaN boxing. This is
-// used for those instructions that produce results in fp registers, but are
-// not really executing an fp operation that requires rounding.
-template <typename RegValue, typename Result, typename Argument>
-inline void RiscVBinaryNaNBoxOp(
-    const Instruction *instruction,
-    std::function<Result(Argument, Argument)> operation) {
-  Argument lhs = GetNaNBoxedSource<RegValue, Argument>(instruction, 0);
-  Argument rhs = GetNaNBoxedSource<RegValue, Argument>(instruction, 1);
-  Result dest_value = operation(lhs, rhs);
-  auto *reg = static_cast<generic::RegisterDestinationOperand<RegValue> *>(
-                  instruction->Destination(0))
-                  ->GetRegister();
-  // Check to see if we need to NaN box the result.
-  if (sizeof(RegValue) > sizeof(Result)) {
-    // If the floating point value is narrower than the register, the upper
-    // bits have to be set to all ones.
-    using UReg = typename std::make_unsigned<RegValue>::type;
-    using UInt = typename FPTypeInfo<Result>::UIntType;
-    auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
-    UReg reg_value = std::numeric_limits<UReg>::max();
-    int shift = 8 * (sizeof(RegValue) - sizeof(Result));
-    reg_value = (reg_value << shift) | dest_u_value;
-    reg->data_buffer()->template Set<RegValue>(0, reg_value);
-    return;
-  }
-  reg->data_buffer()->template Set<Result>(0, dest_value);
-}
-
-// Generic helper function for unary instructions with NaN boxing.
-template <typename DstRegValue, typename SrcRegValue, typename Result,
-          typename Argument>
-inline void RiscVUnaryNaNBoxOp(const Instruction *instruction,
-                               std::function<Result(Argument)> operation) {
-  Argument lhs = GetNaNBoxedSource<SrcRegValue, Argument>(instruction, 0);
-  Result dest_value = operation(lhs);
-  // Check to see if we need to NaN box the result.
-  if (sizeof(DstRegValue) > sizeof(Result)) {
-    // If the floating point value is narrower than the register, the upper
-    // bits have to be set to all ones.
-    using UReg = typename std::make_unsigned<DstRegValue>::type;
-    using UInt = typename FPTypeInfo<Result>::UIntType;
-    auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
-    UReg reg_value = std::numeric_limits<UReg>::max();
-    int shift = 8 * (sizeof(DstRegValue) - sizeof(Result));
-    reg_value = (reg_value << shift) | dest_u_value;
-    WriteCapIntResult(instruction, 0, reg_value);
-    return;
-  }
-  WriteCapIntResult(instruction, 0, dest_value);
-}
-
-// TODO(torerik): Modify as needed if used to produce values in capability
-// registers.
-// Generic helper function for unary floating point instructions. The main
-// difference is that it handles rounding mode and performs NaN boxing.
-template <typename DstRegValue, typename SrcRegValue, typename Result,
-          typename Argument>
-inline void RiscVUnaryFloatNaNBoxOp(const Instruction *instruction,
-                                    std::function<Result(Argument)> operation) {
-  using ResUint = typename FPTypeInfo<Result>::UIntType;
-  Argument lhs = GetNaNBoxedSource<SrcRegValue, Argument>(instruction, 0);
-  // Get the rounding mode.
-  int rm_value = generic::GetInstructionSource<int>(instruction, 1);
-
-  // If the rounding mode is dynamic, read it from the current state.
-  auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
-  if (rm_value == *FPRoundingMode::kDynamic) {
-    if (!rv_fp->rounding_mode_valid()) {
-      LOG(ERROR) << "Invalid rounding mode";
-      return;
-    }
-    rm_value = *(rv_fp->GetRoundingMode());
-  }
-  Result dest_value;
-  {
-    ScopedFPStatus set_fp_status(rv_fp->host_fp_interface(), rm_value);
-    dest_value = operation(lhs);
-  }
-  if (std::isnan(dest_value) && std::signbit(dest_value)) {
-    ResUint res_value = *reinterpret_cast<ResUint *>(&dest_value);
-    res_value &= FPTypeInfo<Result>::kInfMask;
-    dest_value = *reinterpret_cast<Result *>(&res_value);
-  }
-  auto *dest = instruction->Destination(0);
-  auto *reg_dest =
-      static_cast<generic::RegisterDestinationOperand<DstRegValue> *>(dest);
-  auto *reg = reg_dest->GetRegister();
-  // Check to see if we need to NaN box the result.
-  if (sizeof(DstRegValue) > sizeof(Result)) {
-    // If the floating point Value is narrower than the register, the upper
-    // bits have to be set to all ones.
-    using UReg = typename std::make_unsigned<DstRegValue>::type;
-    using UInt = typename FPTypeInfo<Result>::UIntType;
-    auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
-    UReg reg_value = std::numeric_limits<UReg>::max();
-    int shift = 8 * (sizeof(DstRegValue) - sizeof(Result));
-    reg_value = (reg_value << shift) | dest_u_value;
-    reg->data_buffer()->template Set<DstRegValue>(0, reg_value);
-    return;
-  }
-  reg->data_buffer()->template Set<Result>(0, dest_value);
-}
-
-// TODO(torerik): Modify as needed if used to produce values in capability
-// registers.
-// Generic helper function for floating op instructions that do not require
-// NaN boxing since they produce non fp-values.
-template <typename Result, typename Argument>
-inline void RiscVUnaryFloatOp(const Instruction *instruction,
-                              std::function<Result(Argument)> operation) {
-  Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
-  // Get the rounding mode.
-  int rm_value = generic::GetInstructionSource<int>(instruction, 1);
-
-  auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
-  // If the rounding mode is dynamic, read it from the current state.
-  if (rm_value == *FPRoundingMode::kDynamic) {
-    if (!rv_fp->rounding_mode_valid()) {
-      LOG(ERROR) << "Invalid rounding mode";
-      return;
-    }
-    rm_value = *rv_fp->GetRoundingMode();
-  }
-  Result dest_value;
-  {
-    ScopedFPStatus set_fp_status(rv_fp->host_fp_interface(), rm_value);
-    dest_value = operation(lhs);
-  }
-  auto *dest = instruction->Destination(0);
-  using UInt = typename FPTypeInfo<Result>::UIntType;
-  auto *reg_dest =
-      static_cast<generic::RegisterDestinationOperand<UInt> *>(dest);
-  auto *reg = reg_dest->GetRegister();
-  reg->data_buffer()->template Set<Result>(0, dest_value);
-}
-
-// TODO(torerik): Modify as needed if used to produce values in capability
-// registers.
-// Generic helper function for floating op instructions that do not require
-// NaN boxing since they produce non fp-values, but set fflags.
-template <typename Result, typename Argument>
-inline void RiscVUnaryFloatWithFflagsOp(
-    const Instruction *instruction,
-    std::function<Result(Argument, uint32_t &)> operation) {
-  Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
-  // Get the rounding mode.
-  int rm_value = generic::GetInstructionSource<int>(instruction, 1);
-
-  auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
-  // If the rounding mode is dynamic, read it from the current state.
-  if (rm_value == *FPRoundingMode::kDynamic) {
-    if (!rv_fp->rounding_mode_valid()) {
-      LOG(ERROR) << "Invalid rounding mode";
-      return;
-    }
-    rm_value = *rv_fp->GetRoundingMode();
-  }
-  uint32_t flag = 0;
-  Result dest_value;
-  {
-    ScopedFPStatus set_fp_status(rv_fp->host_fp_interface(), rm_value);
-    dest_value = operation(lhs, flag);
-  }
-  auto *dest = instruction->Destination(0);
-  using UInt = typename FPTypeInfo<Result>::UIntType;
-  auto *reg_dest =
-      static_cast<generic::RegisterDestinationOperand<UInt> *>(dest);
-  auto *reg = reg_dest->GetRegister();
-  reg->data_buffer()->template Set<Result>(0, dest_value);
-  auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
-  flag_db->Set<uint32_t>(0, flag);
-  flag_db->Submit();
-}
-
-// TODO(torerik): Modify as needed if used to produce values in capability
-// registers.
-// Generic helper function for binary floating point instructions. The main
-// difference is that it handles rounding mode.
-template <typename Register, typename Result, typename Argument>
-inline void RiscVBinaryFloatNaNBoxOp(
-    const Instruction *instruction,
-    std::function<Result(Argument, Argument)> operation) {
-  Argument lhs = GetNaNBoxedSource<Register, Argument>(instruction, 0);
-  Argument rhs = GetNaNBoxedSource<Register, Argument>(instruction, 1);
-  // Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
-  // Argument rhs = generic::GetInstructionSource<Argument>(instruction, 1);
-
-  // Get the rounding mode.
-  int rm_value = generic::GetInstructionSource<int>(instruction, 2);
-
-  auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
-  // If the rounding mode is dynamic, read it from the current state.
-  if (rm_value == *FPRoundingMode::kDynamic) {
-    if (!rv_fp->rounding_mode_valid()) {
-      LOG(ERROR) << "Invalid rounding mode";
-      return;
-    }
-    rm_value = *rv_fp->GetRoundingMode();
-  }
-  Result dest_value;
-  {
-    ScopedFPStatus fp_status(rv_fp->host_fp_interface(), rm_value);
-    dest_value = operation(lhs, rhs);
-  }
-  if (std::isnan(dest_value)) {
-    *reinterpret_cast<typename FPTypeInfo<Result>::UIntType *>(&dest_value) =
-        FPTypeInfo<Result>::kCanonicalNaN;
-  }
-  auto *reg = static_cast<generic::RegisterDestinationOperand<Register> *>(
-                  instruction->Destination(0))
-                  ->GetRegister();
-  // Check to see if we need to NaN box the result.
-  if (sizeof(Register) > sizeof(Result)) {
-    // If the floating point value is narrower than the register, the upper
-    // bits have to be set to all ones.
-    using UReg = typename std::make_unsigned<Register>::type;
-    using UInt = typename FPTypeInfo<Result>::UIntType;
-    auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
-    UReg reg_value = std::numeric_limits<UReg>::max();
-    int shift = 8 * (sizeof(Register) - sizeof(Result));
-    reg_value = (reg_value << shift) | dest_u_value;
-    reg->data_buffer()->template Set<Register>(0, reg_value);
-    return;
-  }
-  reg->data_buffer()->template Set<Result>(0, dest_value);
-}
-
-// TODO(torerik): Modify as needed if used to produce values in capability
-// registers.
-// Generic helper function for ternary floating point instructions.
-template <typename Register, typename Result, typename Argument>
-inline void RiscVTernaryFloatNaNBoxOp(
-    const Instruction *instruction,
-    std::function<Result(Argument, Argument, Argument)> operation) {
-  Argument rs1 = generic::GetInstructionSource<Argument>(instruction, 0);
-  Argument rs2 = generic::GetInstructionSource<Argument>(instruction, 1);
-  Argument rs3 = generic::GetInstructionSource<Argument>(instruction, 2);
-  // Get the rounding mode.
-  int rm_value = generic::GetInstructionSource<int>(instruction, 3);
-
-  auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
-  // If the rounding mode is dynamic, read it from the current state.
-  if (rm_value == *FPRoundingMode::kDynamic) {
-    if (!rv_fp->rounding_mode_valid()) {
-      LOG(ERROR) << "Invalid rounding mode";
-      return;
-    }
-    rm_value = *rv_fp->GetRoundingMode();
-  }
-  Result dest_value;
-  {
-    ScopedFPStatus fp_status(rv_fp->host_fp_interface(), rm_value);
-    dest_value = operation(rs1, rs2, rs3);
-  }
-  auto *reg = static_cast<generic::RegisterDestinationOperand<Register> *>(
-                  instruction->Destination(0))
-                  ->GetRegister();
-  // Check to see if we need to NaN box the result.
-  if (sizeof(Register) > sizeof(Result)) {
-    // If the floating point value is narrower than the register, the upper
-    // bits have to be set to all ones.
-    using UReg = typename std::make_unsigned<Register>::type;
-    using UInt = typename FPTypeInfo<Result>::UIntType;
-    auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
-    UReg reg_value = std::numeric_limits<UReg>::max();
-    int shift = 8 * (sizeof(Register) - sizeof(Result));
-    reg_value = (reg_value << shift) | dest_u_value;
-    reg->data_buffer()->template Set<Register>(0, reg_value);
-    return;
-  }
-  reg->data_buffer()->template Set<Result>(0, dest_value);
-}
-
-// Helper function to classify floating point values.
-template <typename T>
-typename FPTypeInfo<T>::UIntType ClassifyFP(T val) {
-  using UIntType = typename FPTypeInfo<T>::UIntType;
-  auto int_value = *reinterpret_cast<UIntType *>(&val);
-  UIntType sign = int_value >> (FPTypeInfo<T>::kBitSize - 1);
-  UIntType exp_mask = (1 << FPTypeInfo<T>::kExpSize) - 1;
-  UIntType exp = (int_value >> FPTypeInfo<T>::kSigSize) & exp_mask;
-  UIntType sig =
-      int_value & ((static_cast<UIntType>(1) << FPTypeInfo<T>::kSigSize) - 1);
-  if (exp == 0) {    // The number is denormal or zero.
-    if (sig == 0) {  // The number is zero.
-      return sign ? 1 << 3 : 1 << 4;
-    } else {  // subnormal.
-      return sign ? 1 << 2 : 1 << 5;
-    }
-  } else if (exp == exp_mask) {  //  The number is infinity or NaN.
-    if (sig == 0) {              // infinity
-      return sign ? 1 : 1 << 7;
-    } else {
-      if ((sig >> (FPTypeInfo<T>::kSigSize - 1)) != 0) {  // Quiet NaN.
-        return 1 << 9;
-      } else {  // signaling NaN.
-        return 1 << 8;
-      }
-    }
-  }
-  return sign ? 1 << 1 : 1 << 6;
-}
-
 }  // namespace cheriot
 }  // namespace sim
 }  // namespace mpact
diff --git a/cheriot/test/BUILD b/cheriot/test/BUILD
index a65c979..08d716c 100644
--- a/cheriot/test/BUILD
+++ b/cheriot/test/BUILD
@@ -121,26 +121,6 @@
 )
 
 cc_test(
-    name = "riscv_cheriot_f_instructions_test",
-    size = "small",
-    srcs = [
-        "riscv_cheriot_f_instructions_test.cc",
-    ],
-    copts = [
-        "-ffp-model=strict",
-        "-fprotect-parens",
-    ],
-    tags = ["not_run:arm"],
-    deps = [
-        ":riscv_cheriot_fp_test_base",
-        "//cheriot:riscv_cheriot",
-        "@com_google_googletest//:gtest_main",
-        "@com_google_mpact-riscv//riscv:riscv_state",
-        "@com_google_mpact-sim//mpact/sim/generic:instruction",
-    ],
-)
-
-cc_test(
     name = "riscv_cheriot_zicsr_instructions_test",
     size = "small",
     srcs = [
diff --git a/cheriot/test/riscv_cheriot_f_instructions_test.cc b/cheriot/test/riscv_cheriot_f_instructions_test.cc
deleted file mode 100644
index 4fb1e27..0000000
--- a/cheriot/test/riscv_cheriot_f_instructions_test.cc
+++ /dev/null
@@ -1,347 +0,0 @@
-// Copyright 2024 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cheriot/riscv_cheriot_f_instructions.h"
-
-#include <cmath>
-#include <cstdint>
-#include <tuple>
-
-#include "cheriot/test/riscv_cheriot_fp_test_base.h"
-#include "googlemock/include/gmock/gmock.h"
-#include "mpact/sim/generic/instruction.h"
-#include "riscv//riscv_register.h"
-
-namespace {
-
-using ::mpact::sim::cheriot::test::FPTypeInfo;
-using ::mpact::sim::cheriot::test::OptimizationBarrier;
-using ::mpact::sim::cheriot::test::RiscVFPInstructionTestBase;
-using ::mpact::sim::riscv::FPExceptions;
-using ::mpact::sim::riscv::RVFpRegister;
-
-using ::mpact::sim::cheriot::RiscVFAdd;
-using ::mpact::sim::cheriot::RiscVFClass;
-using ::mpact::sim::cheriot::RiscVFCmpeq;
-using ::mpact::sim::cheriot::RiscVFCmple;
-using ::mpact::sim::cheriot::RiscVFCmplt;
-using ::mpact::sim::cheriot::RiscVFCvtSw;
-using ::mpact::sim::cheriot::RiscVFCvtSwu;
-using ::mpact::sim::cheriot::RiscVFCvtWs;
-using ::mpact::sim::cheriot::RiscVFCvtWus;
-using ::mpact::sim::cheriot::RiscVFDiv;
-using ::mpact::sim::cheriot::RiscVFMadd;
-using ::mpact::sim::cheriot::RiscVFMax;
-using ::mpact::sim::cheriot::RiscVFMin;
-using ::mpact::sim::cheriot::RiscVFMsub;
-using ::mpact::sim::cheriot::RiscVFMul;
-using ::mpact::sim::cheriot::RiscVFNmadd;
-using ::mpact::sim::cheriot::RiscVFNmsub;
-using ::mpact::sim::cheriot::RiscVFSgnj;
-using ::mpact::sim::cheriot::RiscVFSgnjn;
-using ::mpact::sim::cheriot::RiscVFSgnjx;
-using ::mpact::sim::cheriot::RiscVFSqrt;
-using ::mpact::sim::cheriot::RiscVFSub;
-
-class RVCheriot32FInstructionTest : public RiscVFPInstructionTestBase {};
-
-static bool is_snan(float a) {
-  if (!std::isnan(a)) return false;
-  uint32_t ua = *reinterpret_cast<uint32_t *>(&a);
-  if ((ua & (1 << (FPTypeInfo<float>::kSigSize - 1))) == 0) return true;
-  return false;
-}
-
-// Test basic arithmetic instructions.
-TEST_F(RVCheriot32FInstructionTest, RiscVFadd) {
-  SetSemanticFunction(&RiscVFAdd);
-  BinaryOpFPTestHelper<float, float, float>(
-      "fadd", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> float { return lhs + rhs; });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFsub) {
-  SetSemanticFunction(&RiscVFSub);
-  BinaryOpFPTestHelper<float, float, float>(
-      "fsub", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> float { return lhs - rhs; });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFmul) {
-  SetSemanticFunction(&RiscVFMul);
-  BinaryOpFPTestHelper<float, float, float>(
-      "fmul", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> float { return lhs * rhs; });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFdiv) {
-  SetSemanticFunction(&RiscVFDiv);
-  BinaryOpFPTestHelper<float, float, float>(
-      "fdiv", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> float { return lhs / rhs; });
-}
-
-// Test square root.
-TEST_F(RVCheriot32FInstructionTest, RiscVFsqrt) {
-  SetSemanticFunction(&RiscVFSqrt);
-}
-
-// Test Min/Max.
-TEST_F(RVCheriot32FInstructionTest, RiscVFmin) {
-  SetSemanticFunction(&RiscVFMin);
-  BinaryOpWithFflagsFPTestHelper<float, float, float>(
-      "fmin", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> std::tuple<float, uint32_t> {
-        uint32_t flag = 0;
-        if (is_snan(lhs) || is_snan(rhs)) {
-          flag = static_cast<uint32_t>(FPExceptions::kInvalidOp);
-        }
-        if (std::isnan(lhs) && std::isnan(rhs)) {
-          uint32_t val = FPTypeInfo<float>::kCanonicalNaN;
-          return std::tie(*reinterpret_cast<const float *>(&val), flag);
-        }
-        if (std::isnan(lhs)) return std::tie(rhs, flag);
-        if (std::isnan(rhs)) return std::tie(lhs, flag);
-        if ((lhs == 0.0) && (rhs == 0.0)) {
-          return std::tie(std::signbit(lhs) ? lhs : rhs, flag);
-        }
-        return std::tie(lhs > rhs ? rhs : lhs, flag);
-      });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFmax) {
-  SetSemanticFunction(&RiscVFMax);
-  BinaryOpWithFflagsFPTestHelper<float, float, float>(
-      "fmax", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> std::tuple<float, uint32_t> {
-        uint32_t flag = 0;
-        if (is_snan(lhs) || is_snan(rhs)) {
-          flag = static_cast<uint32_t>(FPExceptions::kInvalidOp);
-        }
-        if (std::isnan(lhs) && std::isnan(rhs)) {
-          uint32_t val = FPTypeInfo<float>::kCanonicalNaN;
-          return std::tie(*reinterpret_cast<const float *>(&val), flag);
-        }
-        if (std::isnan(lhs)) return std::tie(rhs, flag);
-        if (std::isnan(rhs)) return std::tie(lhs, flag);
-        if ((lhs == 0.0) && (rhs == 0.0)) {
-          return std::tie(std::signbit(lhs) ? rhs : lhs, flag);
-        }
-        return std::tie(lhs < rhs ? rhs : lhs, flag);
-      });
-}
-
-// Test MAC versions.
-TEST_F(RVCheriot32FInstructionTest, RiscVFMadd) {
-  SetSemanticFunction(&RiscVFMadd);
-  TernaryOpFPTestHelper<float, float, float, float>(
-      "fmadd", instruction_, {"f", "f", "f", "f"}, 32,
-      [](float lhs, float mhs, float rhs) -> float {
-        return OptimizationBarrier(lhs * mhs) + rhs;
-      });
-}
-TEST_F(RVCheriot32FInstructionTest, RiscVFMsub) {
-  SetSemanticFunction(&RiscVFMsub);
-  TernaryOpFPTestHelper<float, float, float, float>(
-      "fmsub", instruction_, {"f", "f", "f", "f"}, 32,
-      [](float lhs, float mhs, float rhs) -> float {
-        return OptimizationBarrier(lhs * mhs) - rhs;
-      });
-}
-TEST_F(RVCheriot32FInstructionTest, RiscVFNmadd) {
-  SetSemanticFunction(&RiscVFNmadd);
-  TernaryOpFPTestHelper<float, float, float, float>(
-      "fnmadd", instruction_, {"f", "f", "f", "f"}, 32,
-      [](float lhs, float mhs, float rhs) -> float {
-        return -OptimizationBarrier(lhs * mhs) - rhs;
-      });
-}
-TEST_F(RVCheriot32FInstructionTest, RiscVFNmsub) {
-  SetSemanticFunction(&RiscVFNmsub);
-  TernaryOpFPTestHelper<float, float, float, float>(
-      "fnmsub", instruction_, {"f", "f", "f", "f"}, 32,
-      [](float lhs, float mhs, float rhs) -> float {
-        return -OptimizationBarrier(lhs * mhs) + rhs;
-      });
-}
-
-// Test conversion instructions.
-// Double to signed 32 bit integer.
-TEST_F(RVCheriot32FInstructionTest, RiscVFCvtWs) {
-  SetSemanticFunction(&RiscVFCvtWs);
-  UnaryOpWithFflagsFPTestHelper<int32_t, float>(
-      "fcvt.w.s", instruction_, {"f", "x"}, 32,
-      [&](float lhs) -> std::tuple<int32_t, uint32_t> {
-        if (std::isnan(lhs))
-          return std::make_tuple(
-              0x7fff'ffff, static_cast<uint32_t>(FPExceptions::kInvalidOp));
-        if (std::isinf(lhs))
-          return std::make_tuple(
-              lhs < 0 ? 0x8000'0000 : 0x7fff'ffff,
-              static_cast<uint32_t>(FPExceptions::kInvalidOp));
-        if (abs(lhs) > static_cast<float>(0x7fff'ffff))
-          return std::make_tuple(
-              lhs < 0 ? 0x8000'0000 : 0x7fff'ffff,
-              static_cast<uint32_t>(FPExceptions::kInvalidOp));
-        uint32_t flag = 0;
-        if (ceil(lhs) != lhs) {
-          flag |= static_cast<uint32_t>(FPExceptions::kInexact);
-          lhs = RoundToInteger(lhs);
-        }
-        return std::make_tuple(static_cast<int32_t>(lhs), flag);
-      });
-}
-
-// Signed 32 bit integer to float.
-TEST_F(RVCheriot32FInstructionTest, RiscVFCvtSw) {
-  SetSemanticFunction(&RiscVFCvtSw);
-  UnaryOpFPTestHelper<float, int32_t>(
-      "fcvt.s.w", instruction_, {"x", "f"}, 32,
-      [](int32_t lhs) -> float { return static_cast<float>(lhs); });
-}
-
-// Double to unsigned 32 bit integer.
-TEST_F(RVCheriot32FInstructionTest, RiscVFCvtWus) {
-  SetSemanticFunction(&RiscVFCvtWus);
-  UnaryOpWithFflagsFPTestHelper<uint32_t, float>(
-      "fcvt.wu.s", instruction_, {"f", "x"}, 32,
-      [&](float lhs) -> std::tuple<uint32_t, uint32_t> {
-        if (std::isnan(lhs))
-          return std::make_tuple(
-              0xffff'ffff, static_cast<uint32_t>(FPExceptions::kInvalidOp));
-        if (lhs < 0) {
-          if ((lhs > -1.0) && (static_cast<int32_t>(lhs) == 0.0)) {
-            return std::make_tuple(
-                0, static_cast<uint32_t>(FPExceptions::kInexact));
-          }
-          return std::make_tuple(
-              0, static_cast<uint32_t>(FPExceptions::kInvalidOp));
-        }
-        if (std::isinf(lhs) || lhs > static_cast<float>(0xffff'ffffUL)) {
-          return std::make_tuple(
-              0xffff'ffff, static_cast<uint32_t>(FPExceptions::kInvalidOp));
-        }
-        uint32_t flag = 0;
-        if (ceil(lhs) != lhs) {
-          flag |= static_cast<uint32_t>(FPExceptions::kInexact);
-          lhs = RoundToInteger(lhs);
-        }
-        return std::make_tuple(static_cast<uint32_t>(lhs), flag);
-      });
-}
-
-// Unsigned 32 bit integer to float.
-TEST_F(RVCheriot32FInstructionTest, RiscVFCvtSwu) {
-  SetSemanticFunction(&RiscVFCvtSwu);
-  UnaryOpFPTestHelper<float, uint32_t>(
-      "fcvt.s.w", instruction_, {"x", "f"}, 32,
-      [](uint32_t lhs) -> float { return static_cast<float>(lhs); });
-}
-
-// Test sign manipulation instructions.
-TEST_F(RVCheriot32FInstructionTest, RiscVFSgnj) {
-  SetSemanticFunction(&RiscVFSgnj);
-  BinaryOpFPTestHelper<float, float, float>(
-      "fsgnj", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> float { return copysign(abs(lhs), rhs); });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFSgnjn) {
-  SetSemanticFunction(&RiscVFSgnjn);
-  BinaryOpFPTestHelper<float, float, float>(
-      "fsgnjn", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> float { return copysign(abs(lhs), -rhs); });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFSgnjx) {
-  SetSemanticFunction(&RiscVFSgnjx);
-  BinaryOpFPTestHelper<float, float, float>(
-      "fsgnjn", instruction_, {"f", "f", "f"}, 32,
-      [](float lhs, float rhs) -> float {
-        auto lhs_u = *reinterpret_cast<uint32_t *>(&lhs);
-        auto rhs_u = *reinterpret_cast<uint32_t *>(&rhs);
-        auto res_u = (lhs_u ^ rhs_u) & 0x8000'0000;
-        auto res = *reinterpret_cast<float *>(&res_u);
-        return copysign(abs(lhs), res);
-      });
-}
-
-// Test compare instructions.
-TEST_F(RVCheriot32FInstructionTest, RiscVFCmpeq) {
-  SetSemanticFunction(&RiscVFCmpeq);
-  BinaryOpWithFflagsFPTestHelper<uint32_t, float, float>(
-      "fcmpeq", instruction_, {"f", "f", "x"}, 32,
-      [](float lhs, float rhs) -> std::tuple<uint32_t, uint32_t> {
-        uint32_t flag = 0;
-        if (is_snan(lhs) || is_snan(rhs)) {
-          flag = static_cast<uint32_t>(FPExceptions::kInvalidOp);
-        }
-        return std::make_tuple(lhs == rhs, flag);
-      });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFCmplt) {
-  SetSemanticFunction(&RiscVFCmplt);
-  BinaryOpWithFflagsFPTestHelper<uint32_t, float, float>(
-      "fcmplt", instruction_, {"f", "f", "x"}, 32,
-      [](float lhs, float rhs) -> std::tuple<uint32_t, uint32_t> {
-        uint32_t flag = 0;
-        if (std::isnan(lhs) || std::isnan(rhs)) {
-          flag = static_cast<uint32_t>(FPExceptions::kInvalidOp);
-        }
-        return std::make_tuple(lhs < rhs, flag);
-      });
-}
-
-TEST_F(RVCheriot32FInstructionTest, RiscVFCmple) {
-  SetSemanticFunction(&RiscVFCmple);
-  BinaryOpWithFflagsFPTestHelper<uint32_t, float, float>(
-      "fcmple", instruction_, {"f", "f", "x"}, 32,
-      [](float lhs, float rhs) -> std::tuple<uint32_t, uint32_t> {
-        uint32_t flag = 0;
-        if (std::isnan(lhs) || std::isnan(rhs)) {
-          flag = static_cast<uint32_t>(FPExceptions::kInvalidOp);
-        }
-        return std::make_tuple(lhs <= rhs, flag);
-      });
-}
-
-// Test class instruction.
-TEST_F(RVCheriot32FInstructionTest, RiscVFClass) {
-  SetSemanticFunction(&RiscVFClass);
-  UnaryOpFPTestHelper<uint32_t, float>(
-      "fclass.d", instruction_, {"f", "x"}, 32, [](float lhs) -> uint32_t {
-        auto fp_class = std::fpclassify(lhs);
-        switch (fp_class) {
-          case FP_INFINITE:
-            return std::signbit(lhs) ? 1 : 1 << 7;
-          case FP_NAN: {
-            auto uint_val =
-                *reinterpret_cast<typename FPTypeInfo<float>::IntType *>(&lhs);
-            bool quiet_nan =
-                (uint_val >> (FPTypeInfo<float>::kSigSize - 1)) & 1;
-            return quiet_nan ? 1 << 9 : 1 << 8;
-          }
-          case FP_ZERO:
-            return std::signbit(lhs) ? 1 << 3 : 1 << 4;
-          case FP_SUBNORMAL:
-            return std::signbit(lhs) ? 1 << 2 : 1 << 5;
-          case FP_NORMAL:
-            return std::signbit(lhs) ? 1 << 1 : 1 << 6;
-        }
-        return 0;
-      });
-}
-
-}  // namespace