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