use super::code::{Precision, Width};
use crate::util::{rotate_left};
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum OffsetError {Unaligned, TooFar}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Offset {width: Width, scaled: u32}
impl Offset {
pub fn new(width: Width, offset: u64) -> Result<Self, OffsetError> {
let shift = width as usize;
let scaled = offset >> shift;
if scaled << shift != offset { return Err(OffsetError::Unaligned); }
const LIMIT: u64 = 1 << 12;
if scaled >= LIMIT { return Err(OffsetError::TooFar); }
Ok(Self {width, scaled: scaled as u32})
}
pub fn width(self) -> Width { self.width }
pub fn scaled(self) -> u32 { self.scaled }
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct ShiftError;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Shift {prec: Precision, amount: u32}
impl Shift {
pub const fn new(prec: Precision, amount: u64) -> Result<Self, ShiftError> {
let num_bits = 5 + (prec as usize);
let limit = 1 << num_bits;
if amount >= limit { return Err(ShiftError); }
Ok(Self {prec: prec, amount: amount as u32})
}
pub const fn prec(self) -> Precision { self.prec }
pub const fn amount(self) -> u32 { self.amount }
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct UnsignedError;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Unsigned<const N: usize>(u32);
impl<const N: usize> Unsigned<N> {
pub fn new(value: u64) -> Result<Self, UnsignedError> {
let _ = 32 - N; let limit = 1 << N;
if value >= limit { return Err(UnsignedError); }
Ok(Self(value as u32))
}
pub fn as_u32(self) -> u32 { self.0 }
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum LogicImmediateError {HighBits, AllSame, NonRepeating}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct LogicImmediate {prec: Precision, encoding: u32}
impl LogicImmediate {
#[allow(clippy::many_single_char_names)]
pub fn new(prec: Precision, mut x: u64) -> Result<Self, LogicImmediateError> {
if prec == Precision::P32 {
if x >> 32 != 0 { return Err(LogicImmediateError::HighBits); }
x |= x << 32;
}
if x == 0 || x == !0 { return Err(LogicImmediateError::AllSame); }
let num_zeros = x.leading_zeros();
x = rotate_left(x, num_zeros);
let num_ones = (!x).leading_zeros();
x = rotate_left(x, num_ones);
let mut y = x;
let num_zeros2 = y.leading_zeros();
y = rotate_left(y, num_zeros2);
let num_ones2 = (!y).leading_zeros();
y = rotate_left(y, num_ones2);
if x != y { return Err(LogicImmediateError::NonRepeating); }
let esize = num_zeros2 + num_ones2;
if !esize.is_power_of_two() || esize < 2 {
return Err(LogicImmediateError::NonRepeating);
}
let r = num_zeros + num_ones;
assert!(r <= esize);
let s = num_ones2 - 1;
assert!(s < esize);
let imms = (128 - 2 * esize + s) & 0x3F;
let immr = r & (esize - 1);
let n = (esize == 64) as u32;
Ok(Self {prec, encoding: (n << 12) | (immr << 6) | imms})
}
pub fn prec(self) -> Precision { self.prec }
pub fn encoding(self) -> u32 { self.encoding }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn logic_immediate() {
use Precision::*;
fn check(prec: Precision, val: u64, expected: u32) {
assert_eq!(
LogicImmediate::new(prec, val).unwrap().encoding(),
expected,
);
}
for prec in [P32, P64] {
for (size, imms_size_bits, imms_length_mask) in [
( 2, 0b111100, 0b000001),
( 4, 0b111000, 0b000011),
( 8, 0b110000, 0b000111),
(16, 0b100000, 0b001111),
(32, 0b000000, 0b011111),
(64, 0b000000, 0b111111),
] {
if size == 64 && prec == P32 {
continue;
}
for length in 0..imms_length_mask {
let pattern = (0..64).step_by(size as usize).fold(
(1 << (length + 1)) - 1,
|acc, shift| acc | (acc << shift));
for rotation in 0..size {
let mut val = crate::util::rotate_right(pattern, rotation);
if prec == P32 { val >>= 32 };
let n = (size == 64) as u32;
let immr = rotation;
let imms = imms_size_bits | length;
let encoding = (n << 12) | (immr << 6) | imms;
check(prec, val, encoding);
}
}
}
}
use LogicImmediateError::*;
assert_eq!(LogicImmediate::new(P32, 0x0000000100000001), Err(HighBits));
assert_eq!(LogicImmediate::new(P32, 0x00000000), Err(AllSame));
assert_eq!(LogicImmediate::new(P32, 0xFFFFFFFF), Err(AllSame));
assert_eq!(LogicImmediate::new(P32, 0x5A5A5A5A), Err(NonRepeating));
assert_eq!(LogicImmediate::new(P32, 0x00000005), Err(NonRepeating));
assert_eq!(LogicImmediate::new(P64, 0x0000000000000000), Err(AllSame));
assert_eq!(LogicImmediate::new(P64, 0xFFFFFFFFFFFFFFFF), Err(AllSame));
assert_eq!(LogicImmediate::new(P64, 0x5A5A5A5A5A5A5A5A), Err(NonRepeating));
assert_eq!(LogicImmediate::new(P64, 0x0000000000000005), Err(NonRepeating));
}
}