| //! Riscv64 ISA definitions: immediate constants. |
| |
| // Some variants are never constructed, but we still want them as options in the future. |
| use super::Inst; |
| #[allow(dead_code)] |
| use std::fmt::{Debug, Display, Formatter, Result}; |
| |
| #[derive(Copy, Clone, Debug, Default)] |
| pub struct Imm12 { |
| /// 16-bit container where the low 12 bits are the data payload. |
| /// |
| /// Acquiring the underlying value requires sign-extending the 12th bit. |
| bits: u16, |
| } |
| |
| impl Imm12 { |
| pub(crate) const ZERO: Self = Self { bits: 0 }; |
| pub(crate) const ONE: Self = Self { bits: 1 }; |
| |
| pub fn maybe_from_u64(val: u64) -> Option<Imm12> { |
| Self::maybe_from_i64(val as i64) |
| } |
| |
| pub fn maybe_from_i64(val: i64) -> Option<Imm12> { |
| if val >= -2048 && val <= 2047 { |
| Some(Imm12 { |
| bits: val as u16 & 0xfff, |
| }) |
| } else { |
| None |
| } |
| } |
| |
| #[inline] |
| pub fn from_i16(bits: i16) -> Self { |
| assert!(bits >= -2048 && bits <= 2047); |
| Self { |
| bits: (bits & 0xfff) as u16, |
| } |
| } |
| |
| #[inline] |
| pub fn as_i16(self) -> i16 { |
| (self.bits << 4) as i16 >> 4 |
| } |
| |
| #[inline] |
| pub fn bits(&self) -> u32 { |
| self.bits.into() |
| } |
| } |
| |
| impl Into<i64> for Imm12 { |
| fn into(self) -> i64 { |
| self.as_i16().into() |
| } |
| } |
| |
| impl Display for Imm12 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{:+}", self.as_i16()) |
| } |
| } |
| |
| // singed |
| #[derive(Clone, Copy, Default)] |
| pub struct Imm20 { |
| /// 32-bit container where the low 20 bits are the data payload. |
| /// |
| /// Acquiring the underlying value requires sign-extending the 20th bit. |
| bits: u32, |
| } |
| |
| impl Imm20 { |
| pub(crate) const ZERO: Self = Self { bits: 0 }; |
| |
| pub fn maybe_from_u64(val: u64) -> Option<Imm20> { |
| Self::maybe_from_i64(val as i64) |
| } |
| |
| pub fn maybe_from_i64(val: i64) -> Option<Imm20> { |
| if val >= -(0x7_ffff + 1) && val <= 0x7_ffff { |
| Some(Imm20 { bits: val as u32 }) |
| } else { |
| None |
| } |
| } |
| |
| #[inline] |
| pub fn from_i32(bits: i32) -> Self { |
| assert!(bits >= -(0x7_ffff + 1) && bits <= 0x7_ffff); |
| Self { |
| bits: (bits as u32) & 0xf_ffff, |
| } |
| } |
| |
| #[inline] |
| pub fn as_i32(&self) -> i32 { |
| ((self.bits << 12) as i32) >> 12 |
| } |
| |
| #[inline] |
| pub fn bits(&self) -> u32 { |
| self.bits |
| } |
| } |
| |
| impl Debug for Imm20 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.as_i32()) |
| } |
| } |
| |
| impl Display for Imm20 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.bits) |
| } |
| } |
| |
| /// An unsigned 5-bit immediate. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct UImm5 { |
| value: u8, |
| } |
| |
| impl UImm5 { |
| /// Create an unsigned 5-bit immediate from u8. |
| pub fn maybe_from_u8(value: u8) -> Option<UImm5> { |
| if value < 32 { |
| Some(UImm5 { value }) |
| } else { |
| None |
| } |
| } |
| |
| /// Bits for encoding. |
| pub fn bits(&self) -> u32 { |
| u32::from(self.value) |
| } |
| } |
| |
| impl Display for UImm5 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.value) |
| } |
| } |
| |
| /// A Signed 5-bit immediate. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct Imm5 { |
| value: i8, |
| } |
| |
| impl Imm5 { |
| /// Create an signed 5-bit immediate from an i8. |
| pub fn maybe_from_i8(value: i8) -> Option<Imm5> { |
| if value >= -16 && value <= 15 { |
| Some(Imm5 { value }) |
| } else { |
| None |
| } |
| } |
| |
| pub fn from_bits(value: u8) -> Imm5 { |
| assert_eq!(value & 0x1f, value); |
| let signed = ((value << 3) as i8) >> 3; |
| Imm5 { value: signed } |
| } |
| |
| /// Bits for encoding. |
| pub fn bits(&self) -> u8 { |
| self.value as u8 & 0x1f |
| } |
| } |
| |
| impl Display for Imm5 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.value) |
| } |
| } |
| |
| /// A Signed 6-bit immediate. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct Imm6 { |
| value: i8, |
| } |
| |
| impl Imm6 { |
| /// Create an signed 6-bit immediate from an i16 |
| pub fn maybe_from_i16(value: i16) -> Option<Self> { |
| if value >= -32 && value <= 31 { |
| Some(Self { value: value as i8 }) |
| } else { |
| None |
| } |
| } |
| |
| pub fn maybe_from_i32(value: i32) -> Option<Self> { |
| value.try_into().ok().and_then(Imm6::maybe_from_i16) |
| } |
| |
| pub fn maybe_from_i64(value: i64) -> Option<Self> { |
| value.try_into().ok().and_then(Imm6::maybe_from_i16) |
| } |
| |
| pub fn maybe_from_imm12(value: Imm12) -> Option<Self> { |
| Imm6::maybe_from_i16(value.as_i16()) |
| } |
| |
| /// Bits for encoding. |
| pub fn bits(&self) -> u8 { |
| self.value as u8 & 0x3f |
| } |
| } |
| |
| impl Display for Imm6 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.value) |
| } |
| } |
| |
| /// A unsigned 6-bit immediate. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct Uimm6 { |
| value: u8, |
| } |
| |
| impl Uimm6 { |
| /// Create an unsigned 6-bit immediate from an u8 |
| pub fn maybe_from_u8(value: u8) -> Option<Self> { |
| if value <= 63 { |
| Some(Self { value }) |
| } else { |
| None |
| } |
| } |
| |
| /// Bits for encoding. |
| pub fn bits(&self) -> u8 { |
| self.value & 0x3f |
| } |
| } |
| |
| impl Display for Uimm6 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.value) |
| } |
| } |
| |
| /// A unsigned 5-bit immediate. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct Uimm5 { |
| value: u8, |
| } |
| |
| impl Uimm5 { |
| /// Create an unsigned 5-bit immediate from an u8 |
| pub fn maybe_from_u8(value: u8) -> Option<Self> { |
| if value <= 31 { |
| Some(Self { value }) |
| } else { |
| None |
| } |
| } |
| |
| /// Bits for encoding. |
| pub fn bits(&self) -> u8 { |
| self.value & 0x1f |
| } |
| } |
| |
| impl Display for Uimm5 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.value) |
| } |
| } |
| |
| /// A unsigned 2-bit immediate. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct Uimm2 { |
| value: u8, |
| } |
| |
| impl Uimm2 { |
| /// Create an unsigned 2-bit immediate from an u8 |
| pub fn maybe_from_u8(value: u8) -> Option<Self> { |
| if value <= 3 { |
| Some(Self { value }) |
| } else { |
| None |
| } |
| } |
| |
| /// Bits for encoding. |
| pub fn bits(&self) -> u8 { |
| self.value & 0x3 |
| } |
| } |
| |
| impl Display for Uimm2 { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| write!(f, "{}", self.value) |
| } |
| } |
| |
| impl Inst { |
| pub(crate) fn imm_min() -> i64 { |
| let imm20_max: i64 = (1 << 19) << 12; |
| let imm12_max = 1 << 11; |
| -imm20_max - imm12_max |
| } |
| pub(crate) fn imm_max() -> i64 { |
| let imm20_max: i64 = ((1 << 19) - 1) << 12; |
| let imm12_max = (1 << 11) - 1; |
| imm20_max + imm12_max |
| } |
| |
| /// An imm20 immediate and an Imm12 immediate can generate a 32-bit immediate. |
| /// This helper produces an imm12, imm20, or both to generate the value. |
| /// |
| /// `value` must be between `imm_min()` and `imm_max()`, or else |
| /// this helper returns `None`. |
| pub(crate) fn generate_imm(value: u64) -> Option<(Imm20, Imm12)> { |
| if let Some(imm12) = Imm12::maybe_from_u64(value) { |
| // can be load using single imm12. |
| return Some((Imm20::ZERO, imm12)); |
| } |
| let value = value as i64; |
| if !(value >= Self::imm_min() && value <= Self::imm_max()) { |
| // not in range, return None. |
| return None; |
| } |
| const MOD_NUM: i64 = 4096; |
| let (imm20, imm12) = if value > 0 { |
| let mut imm20 = value / MOD_NUM; |
| let mut imm12 = value % MOD_NUM; |
| if imm12 >= 2048 { |
| imm12 -= MOD_NUM; |
| imm20 += 1; |
| } |
| assert!(imm12 >= -2048 && imm12 <= 2047); |
| (imm20, imm12) |
| } else { |
| // this is the abs value. |
| let value_abs = value.abs(); |
| let imm20 = value_abs / MOD_NUM; |
| let imm12 = value_abs % MOD_NUM; |
| let mut imm20 = -imm20; |
| let mut imm12 = -imm12; |
| if imm12 < -2048 { |
| imm12 += MOD_NUM; |
| imm20 -= 1; |
| } |
| (imm20, imm12) |
| }; |
| assert!(imm20 != 0 || imm12 != 0); |
| let imm20 = i32::try_from(imm20).unwrap(); |
| let imm12 = i16::try_from(imm12).unwrap(); |
| Some((Imm20::from_i32(imm20), Imm12::from_i16(imm12))) |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| #[test] |
| fn test_imm12() { |
| let x = Imm12::ZERO; |
| assert_eq!(0, x.bits()); |
| Imm12::maybe_from_u64(0xffff_ffff_ffff_ffff).unwrap(); |
| } |
| |
| #[test] |
| fn imm20_and_imm12() { |
| assert!(Inst::imm_max() == (i32::MAX - 2048) as i64); |
| assert!(Inst::imm_min() == i32::MIN as i64 - 2048); |
| } |
| } |