diff --git a/CHANGELOG.md b/CHANGELOG.md index 57aaedde..bd788989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Removed - User mode registers removed, as they are no longer supported in RISC-V +- FCSR register operations removed to avoid UB (#148) ## [v0.10.1] - 2023-01-18 diff --git a/src/register.rs b/src/register.rs index 85afc516..d38db22d 100644 --- a/src/register.rs +++ b/src/register.rs @@ -10,14 +10,35 @@ //! - minstreth //! - mhpmcounter<3-31>h //! - mstatush +//! +//! # On Floating-Point CSRs +//! +//! We are deliberately *not* providing instructions that could change the floating-point rounding +//! mode or exception behavior or read the accrued exceptions flags: `frcsr`, `fscsr`, `fsrm`, +//! `frflags`, `fsflags`. +//! +//! Rust makes no guarantees whatsoever about the contents of the accrued exceptions register: Rust +//! floating-point operations may or may not result in this register getting updated with exception +//! state, and the register can change between two invocations of this function even when no +//! floating-point operations appear in the source code (since floating-point operations appearing +//! earlier or later can be reordered). +//! +//! Modifying the rounding mode leads to **immediate Undefined Behavior**: Rust assumes that the +//! default rounding mode is always set and will optimize accordingly. This even applies when the +//! rounding mode is altered and later reset to its original value without any floating-point +//! operations appearing in the source code between those operations (since floating-point +//! operations appearing earlier or later can be reordered). +//! +//! If you need to perform some floating-point operations and check whether they raised an +//! exception, use a single inline assembly block for the entire sequence of operations. +//! +//! If you need to perform some floating-point operations under a different rounding mode, use a +//! single inline assembly block and make sure to restore the original rounding mode before the end +//! of the block. #[macro_use] mod macros; -// User Floating-Point CSRs -// TODO: frm, fflags -pub mod fcsr; - // User Counter/Timers pub mod cycle; pub mod cycleh; @@ -29,7 +50,6 @@ pub mod time; pub mod timeh; // Supervisor Trap Setup -// TODO: sedeleg, sideleg pub mod scounteren; pub mod sie; pub mod sstatus; diff --git a/src/register/fcsr.rs b/src/register/fcsr.rs deleted file mode 100644 index ccb649a1..00000000 --- a/src/register/fcsr.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! Floating-point control and status register - -/// Floating-point control and status register -#[derive(Clone, Copy, Debug)] -pub struct FCSR { - bits: u32, -} - -/// Accrued Exception Flags -#[derive(Clone, Copy, Debug)] -pub struct Flags(u32); - -/// Accrued Exception Flag -#[derive(Clone, Copy, Debug)] -pub enum Flag { - /// Inexact - NX = 0b00001, - - /// Underflow - UF = 0b00010, - - /// Overflow - OF = 0b00100, - - /// Divide by Zero - DZ = 0b01000, - - /// Invalid Operation - NV = 0b10000, -} - -impl Flags { - /// Inexact - #[inline] - pub fn nx(&self) -> bool { - self.0 & (1 << 0) != 0 - } - - /// Underflow - #[inline] - pub fn uf(&self) -> bool { - self.0 & (1 << 1) != 0 - } - - /// Overflow - #[inline] - pub fn of(&self) -> bool { - self.0 & (1 << 2) != 0 - } - - /// Divide by Zero - #[inline] - pub fn dz(&self) -> bool { - self.0 & (1 << 3) != 0 - } - - /// Invalid Operation - #[inline] - pub fn nv(&self) -> bool { - self.0 & (1 << 4) != 0 - } -} - -/// Rounding Mode -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum RoundingMode { - RoundToNearestEven = 0b000, - RoundTowardsZero = 0b001, - RoundDown = 0b010, - RoundUp = 0b011, - RoundToNearestMaxMagnitude = 0b100, - Invalid = 0b111, -} - -impl FCSR { - /// Returns the contents of the register as raw bits - #[inline] - pub fn bits(&self) -> u32 { - self.bits - } - - /// Accrued Exception Flags - #[inline] - pub fn fflags(&self) -> Flags { - Flags(self.bits & 0x1F) // bits 0-4 - } - - /// Rounding Mode - #[inline] - pub fn frm(&self) -> RoundingMode { - let frm = (self.bits >> 5) & 0x7; // bits 5-7 - match frm { - 0b000 => RoundingMode::RoundToNearestEven, - 0b001 => RoundingMode::RoundTowardsZero, - 0b010 => RoundingMode::RoundDown, - 0b011 => RoundingMode::RoundUp, - 0b100 => RoundingMode::RoundToNearestMaxMagnitude, - _ => RoundingMode::Invalid, - } - } -} - -read_csr!(0x003); -write_csr!(0x003); -clear!(0x003); - -/// Reads the CSR -#[inline] -pub fn read() -> FCSR { - FCSR { - bits: unsafe { _read() as u32 }, - } -} - -/// Writes the CSR -#[inline] -pub unsafe fn set_rounding_mode(frm: RoundingMode) { - let old = read(); - let bits = ((frm as u32) << 5) | old.fflags().0; - _write(bits as usize); -} - -/// Resets `fflags` field bits -#[inline] -pub unsafe fn clear_flags() { - let mask = 0b11111; - _clear(mask); -} - -/// Resets `fflags` field bit -#[inline] -pub unsafe fn clear_flag(flag: Flag) { - _clear(flag as usize); -}