machine_check/types/
signed.rs

1use std::{
2    fmt::Debug,
3    ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub},
4};
5
6use machine_check_common::{PANIC_MSG_DIV_BY_ZERO, PANIC_MSG_REM_BY_ZERO};
7use mck::{
8    concr::{self, IntoMck},
9    forward::{Bitwise, HwArith, HwShift},
10};
11
12use crate::{traits::Ext, Bitvector, Unsigned};
13
14/// Signed bitvector.
15///
16/// The number of bits is specified in the generic parameter L.
17/// Signed bitvectors support bitwise operations and wrapping-arithmetic operations.
18/// Arithmetic bit extension is also possible (the sign bit is copied into any bits above it).
19/// Signed bitvectors be converted into [`Unsigned`] or [`Bitvector`].
20///
21/// Currently, it is not possible to create signed bitvectors directly, only convert into them.
22#[derive(Clone, Copy, Hash, PartialEq, Eq)]
23pub struct Signed<const L: u32>(pub(super) concr::Bitvector<L>);
24
25// --- BITWISE OPERATIONS ---
26
27impl<const L: u32> Not for Signed<L> {
28    type Output = Self;
29
30    /// Performs bitwise NOT.
31    fn not(self) -> Self::Output {
32        Self(self.0.bit_not())
33    }
34}
35
36impl<const L: u32> BitAnd<Signed<L>> for Signed<L> {
37    type Output = Self;
38
39    /// Performs bitwise AND.
40    fn bitand(self, rhs: Signed<L>) -> Self::Output {
41        Self(self.0.bit_and(rhs.0))
42    }
43}
44impl<const L: u32> BitOr<Signed<L>> for Signed<L> {
45    type Output = Self;
46
47    /// Performs bitwise OR.
48    fn bitor(self, rhs: Signed<L>) -> Self::Output {
49        Self(self.0.bit_or(rhs.0))
50    }
51}
52impl<const L: u32> BitXor<Signed<L>> for Signed<L> {
53    type Output = Self;
54
55    /// Performs bitwise XOR.
56    fn bitxor(self, rhs: Signed<L>) -> Self::Output {
57        Self(self.0.bit_xor(rhs.0))
58    }
59}
60
61// --- ARITHMETIC OPERATIONS ---
62
63impl<const L: u32> Add<Signed<L>> for Signed<L> {
64    type Output = Self;
65
66    /// Performs wrapping addition.
67    fn add(self, rhs: Signed<L>) -> Self::Output {
68        Self(self.0.add(rhs.0))
69    }
70}
71
72impl<const L: u32> Sub<Signed<L>> for Signed<L> {
73    type Output = Self;
74
75    /// Performs wrapping subtraction.
76    fn sub(self, rhs: Signed<L>) -> Self::Output {
77        Self(self.0.sub(rhs.0))
78    }
79}
80
81impl<const L: u32> Mul<Signed<L>> for Signed<L> {
82    type Output = Self;
83
84    /// Performs wrapping multiplication.
85    fn mul(self, rhs: Signed<L>) -> Self::Output {
86        Self(self.0.mul(rhs.0))
87    }
88}
89
90impl<const L: u32> Div<Signed<L>> for Signed<L> {
91    type Output = Self;
92
93    /// Performs wrapping signed division.
94    ///
95    /// This behaves as Rust `wrapping_div`, where the result of `MIN / -1`,
96    /// which would be unrepresentable `-MIN`, is wrapped to `MIN`.
97    ///
98    /// # Panics
99    ///
100    /// Panics if `rhs` is zero.
101    fn div(self, rhs: Signed<L>) -> Self::Output {
102        let panic_result = self.0.sdiv(rhs.0);
103        if panic_result.panic.is_nonzero() {
104            panic!("{}", PANIC_MSG_DIV_BY_ZERO)
105        }
106        Self(panic_result.result)
107    }
108}
109
110impl<const L: u32> Rem<Signed<L>> for Signed<L> {
111    type Output = Self;
112
113    /// Performs wrapping signed division.
114    ///
115    /// This behaves as Rust `wrapping_rem`, where the result of `MIN % -1`,
116    /// is defined as 0.
117    ///
118    /// # Panics
119    ///
120    /// Panics if `rhs` is zero.
121    fn rem(self, rhs: Signed<L>) -> Self::Output {
122        let panic_result = self.0.srem(rhs.0);
123        if panic_result.panic.is_nonzero() {
124            panic!("{}", PANIC_MSG_REM_BY_ZERO)
125        }
126        Self(panic_result.result)
127    }
128}
129
130impl<const L: u32> Shl<Signed<L>> for Signed<L> {
131    type Output = Self;
132
133    /// Performs a left shift.
134    ///
135    /// Unlike a right shift, where the behaviour is dependent on signedness,
136    /// the left shift has the same behaviour: shifted-out bits on the left
137    /// are discarded and zeros are shifted in on the right.
138    ///
139    /// The right-hand side operand is interpreted as unsigned and if it
140    /// is equal or greater to the bit-width, the result is all-zeros,
141    /// as in Rust `unbounded_shl`. It is planned to restrict the bit-width
142    /// in the future so that this edge case can never occur.
143    ///
144    /// Note that this means that shifting left with a negative right operand
145    /// produces an all-zeros value.
146    fn shl(self, rhs: Signed<L>) -> Self::Output {
147        Self(self.0.logic_shl(rhs.0))
148    }
149}
150
151impl<const L: u32> Shr<Signed<L>> for Signed<L> {
152    type Output = Self;
153
154    /// Performs an arithmetic right shift.
155    ///
156    /// The right-hand side operand is interpreted as unsigned and if it
157    /// is equal or greater to the bit-width, the result is all-zeros or
158    /// all-ones depending on the original sign bit, as in Rust `unbounded_shr`
159    /// on signed primitives.
160    /// It is planned to restrict the bit-width in the future so that this edge
161    /// case can never occur.
162    ///
163    /// Note that this means that shifting right with a negative right operand
164    /// produces an all-zeros or all-ones value, depending on the original sign bit.
165    fn shr(self, rhs: Signed<L>) -> Self::Output {
166        Self(self.0.arith_shr(rhs.0))
167    }
168}
169
170// --- EXTENSION ---
171impl<const L: u32, const X: u32> Ext<X> for Signed<L> {
172    type Output = Signed<X>;
173
174    /// Extends or narrows the bit-vector.
175    ///
176    /// If an extension is performed, the upper bits
177    /// are the copy of the original sign bit.
178    fn ext(self) -> Self::Output {
179        Signed::<X>(mck::forward::Ext::sext(self.0))
180    }
181}
182
183// --- ORDERING ---
184
185impl<const L: u32> PartialOrd for Signed<L> {
186    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
187        Some(self.cmp(other))
188    }
189}
190
191impl<const L: u32> Ord for Signed<L> {
192    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
193        self.0.signed_cmp(&other.0)
194    }
195}
196
197// --- CONVERSION ---
198impl<const L: u32> From<Unsigned<L>> for Signed<L> {
199    /// Converts the signedness information from `Unsigned` to `Signed`.
200    fn from(value: Unsigned<L>) -> Self {
201        Self(value.0)
202    }
203}
204
205impl<const L: u32> From<Bitvector<L>> for Signed<L> {
206    /// Adds signedness information to `Bitvector`.
207    fn from(value: Bitvector<L>) -> Self {
208        Self(value.0)
209    }
210}
211
212// --- MISC ---
213
214impl<const L: u32> Debug for Signed<L> {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        std::fmt::Debug::fmt(&self.0, f)
217    }
218}
219
220// --- INTERNAL IMPLEMENTATIONS ---
221
222#[doc(hidden)]
223impl<const L: u32> IntoMck for Signed<L> {
224    type Type = mck::concr::Bitvector<L>;
225
226    fn into_mck(self) -> Self::Type {
227        self.0
228    }
229}