cranelift_codegen/isa/riscv64/inst/
imms.rs

1//! Riscv64 ISA definitions: immediate constants.
2
3// Some variants are never constructed, but we still want them as options in the future.
4use super::Inst;
5use std::fmt::{Debug, Display, Formatter, Result};
6
7#[derive(Copy, Clone, Debug, Default)]
8pub struct Imm12 {
9    /// 16-bit container where the low 12 bits are the data payload.
10    ///
11    /// Acquiring the underlying value requires sign-extending the 12th bit.
12    bits: u16,
13}
14
15impl Imm12 {
16    pub(crate) const ZERO: Self = Self { bits: 0 };
17    pub(crate) const ONE: Self = Self { bits: 1 };
18
19    pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
20        Self::maybe_from_i64(val as i64)
21    }
22
23    pub fn maybe_from_i64(val: i64) -> Option<Imm12> {
24        if val >= -2048 && val <= 2047 {
25            Some(Imm12 {
26                bits: val as u16 & 0xfff,
27            })
28        } else {
29            None
30        }
31    }
32
33    #[inline]
34    pub fn from_i16(bits: i16) -> Self {
35        assert!(bits >= -2048 && bits <= 2047);
36        Self {
37            bits: (bits & 0xfff) as u16,
38        }
39    }
40
41    #[inline]
42    pub fn as_i16(self) -> i16 {
43        (self.bits << 4) as i16 >> 4
44    }
45
46    #[inline]
47    pub fn bits(&self) -> u32 {
48        self.bits.into()
49    }
50}
51
52impl Into<i64> for Imm12 {
53    fn into(self) -> i64 {
54        self.as_i16().into()
55    }
56}
57
58impl Display for Imm12 {
59    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
60        write!(f, "{:+}", self.as_i16())
61    }
62}
63
64// signed
65#[derive(Clone, Copy, Default)]
66pub struct Imm20 {
67    /// 32-bit container where the low 20 bits are the data payload.
68    ///
69    /// Acquiring the underlying value requires sign-extending the 20th bit.
70    bits: u32,
71}
72
73impl Imm20 {
74    pub(crate) const ZERO: Self = Self { bits: 0 };
75
76    pub fn maybe_from_u64(val: u64) -> Option<Imm20> {
77        Self::maybe_from_i64(val as i64)
78    }
79
80    pub fn maybe_from_i64(val: i64) -> Option<Imm20> {
81        if val >= -(0x7_ffff + 1) && val <= 0x7_ffff {
82            Some(Imm20 { bits: val as u32 })
83        } else {
84            None
85        }
86    }
87
88    #[inline]
89    pub fn from_i32(bits: i32) -> Self {
90        assert!(bits >= -(0x7_ffff + 1) && bits <= 0x7_ffff);
91        Self {
92            bits: (bits as u32) & 0xf_ffff,
93        }
94    }
95
96    #[inline]
97    pub fn as_i32(&self) -> i32 {
98        ((self.bits << 12) as i32) >> 12
99    }
100
101    #[inline]
102    pub fn bits(&self) -> u32 {
103        self.bits
104    }
105}
106
107impl Debug for Imm20 {
108    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
109        write!(f, "{}", self.as_i32())
110    }
111}
112
113impl Display for Imm20 {
114    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
115        write!(f, "{}", self.bits)
116    }
117}
118
119/// An unsigned 5-bit immediate.
120#[derive(Clone, Copy, Debug, PartialEq)]
121pub struct UImm5 {
122    value: u8,
123}
124
125impl UImm5 {
126    /// Create an unsigned 5-bit immediate from u8.
127    pub fn maybe_from_u8(value: u8) -> Option<UImm5> {
128        if value < 32 {
129            Some(UImm5 { value })
130        } else {
131            None
132        }
133    }
134
135    /// Bits for encoding.
136    pub fn bits(&self) -> u32 {
137        u32::from(self.value)
138    }
139}
140
141impl Display for UImm5 {
142    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
143        write!(f, "{}", self.value)
144    }
145}
146
147/// A Signed 5-bit immediate.
148#[derive(Clone, Copy, Debug, PartialEq)]
149pub struct Imm5 {
150    value: i8,
151}
152
153impl Imm5 {
154    /// Create an signed 5-bit immediate from an i8.
155    pub fn maybe_from_i8(value: i8) -> Option<Imm5> {
156        if value >= -16 && value <= 15 {
157            Some(Imm5 { value })
158        } else {
159            None
160        }
161    }
162
163    pub fn from_bits(value: u8) -> Imm5 {
164        assert_eq!(value & 0x1f, value);
165        let signed = ((value << 3) as i8) >> 3;
166        Imm5 { value: signed }
167    }
168
169    /// Bits for encoding.
170    pub fn bits(&self) -> u8 {
171        self.value as u8 & 0x1f
172    }
173}
174
175impl Display for Imm5 {
176    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
177        write!(f, "{}", self.value)
178    }
179}
180
181/// A Signed 6-bit immediate.
182#[derive(Clone, Copy, Debug, PartialEq)]
183pub struct Imm6 {
184    value: i8,
185}
186
187impl Imm6 {
188    /// Create an signed 6-bit immediate from an i16
189    pub fn maybe_from_i16(value: i16) -> Option<Self> {
190        if value >= -32 && value <= 31 {
191            Some(Self { value: value as i8 })
192        } else {
193            None
194        }
195    }
196
197    pub fn maybe_from_i32(value: i32) -> Option<Self> {
198        value.try_into().ok().and_then(Imm6::maybe_from_i16)
199    }
200
201    pub fn maybe_from_imm12(value: Imm12) -> Option<Self> {
202        Imm6::maybe_from_i16(value.as_i16())
203    }
204
205    /// Bits for encoding.
206    pub fn bits(&self) -> u8 {
207        self.value as u8 & 0x3f
208    }
209}
210
211impl Display for Imm6 {
212    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
213        write!(f, "{}", self.value)
214    }
215}
216
217/// A unsigned 6-bit immediate.
218#[derive(Clone, Copy, Debug, PartialEq)]
219pub struct Uimm6 {
220    value: u8,
221}
222
223impl Uimm6 {
224    /// Create an unsigned 6-bit immediate from an u8
225    pub fn maybe_from_u8(value: u8) -> Option<Self> {
226        if value <= 63 {
227            Some(Self { value })
228        } else {
229            None
230        }
231    }
232
233    /// Bits for encoding.
234    pub fn bits(&self) -> u8 {
235        self.value & 0x3f
236    }
237}
238
239impl Display for Uimm6 {
240    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
241        write!(f, "{}", self.value)
242    }
243}
244
245/// A unsigned 5-bit immediate.
246#[derive(Clone, Copy, Debug, PartialEq)]
247pub struct Uimm5 {
248    value: u8,
249}
250
251impl Uimm5 {
252    /// Create an unsigned 5-bit immediate from an u8
253    pub fn maybe_from_u8(value: u8) -> Option<Self> {
254        if value <= 31 {
255            Some(Self { value })
256        } else {
257            None
258        }
259    }
260
261    /// Bits for encoding.
262    pub fn bits(&self) -> u8 {
263        self.value & 0x1f
264    }
265}
266
267impl Display for Uimm5 {
268    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
269        write!(f, "{}", self.value)
270    }
271}
272
273/// A unsigned 2-bit immediate.
274#[derive(Clone, Copy, Debug, PartialEq)]
275pub struct Uimm2 {
276    value: u8,
277}
278
279impl Uimm2 {
280    /// Create an unsigned 2-bit immediate from an u8
281    pub fn maybe_from_u8(value: u8) -> Option<Self> {
282        if value <= 3 {
283            Some(Self { value })
284        } else {
285            None
286        }
287    }
288
289    /// Bits for encoding.
290    pub fn bits(&self) -> u8 {
291        self.value & 0x3
292    }
293}
294
295impl Display for Uimm2 {
296    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
297        write!(f, "{}", self.value)
298    }
299}
300
301impl Inst {
302    pub(crate) fn imm_min() -> i64 {
303        let imm20_max: i64 = (1 << 19) << 12;
304        let imm12_max = 1 << 11;
305        -imm20_max - imm12_max
306    }
307    pub(crate) fn imm_max() -> i64 {
308        let imm20_max: i64 = ((1 << 19) - 1) << 12;
309        let imm12_max = (1 << 11) - 1;
310        imm20_max + imm12_max
311    }
312
313    /// An imm20 immediate and an Imm12 immediate can generate a 32-bit immediate.
314    /// This helper produces an imm12, imm20, or both to generate the value.
315    ///
316    /// `value` must be between `imm_min()` and `imm_max()`, or else
317    /// this helper returns `None`.
318    pub(crate) fn generate_imm(value: u64) -> Option<(Imm20, Imm12)> {
319        if let Some(imm12) = Imm12::maybe_from_u64(value) {
320            // can be load using single imm12.
321            return Some((Imm20::ZERO, imm12));
322        }
323        let value = value as i64;
324        if !(value >= Self::imm_min() && value <= Self::imm_max()) {
325            // not in range, return None.
326            return None;
327        }
328        const MOD_NUM: i64 = 4096;
329        let (imm20, imm12) = if value > 0 {
330            let mut imm20 = value / MOD_NUM;
331            let mut imm12 = value % MOD_NUM;
332            if imm12 >= 2048 {
333                imm12 -= MOD_NUM;
334                imm20 += 1;
335            }
336            assert!(imm12 >= -2048 && imm12 <= 2047);
337            (imm20, imm12)
338        } else {
339            // this is the abs value.
340            let value_abs = value.abs();
341            let imm20 = value_abs / MOD_NUM;
342            let imm12 = value_abs % MOD_NUM;
343            let mut imm20 = -imm20;
344            let mut imm12 = -imm12;
345            if imm12 < -2048 {
346                imm12 += MOD_NUM;
347                imm20 -= 1;
348            }
349            (imm20, imm12)
350        };
351        assert!(imm20 != 0 || imm12 != 0);
352        let imm20 = i32::try_from(imm20).unwrap();
353        let imm12 = i16::try_from(imm12).unwrap();
354        Some((Imm20::from_i32(imm20), Imm12::from_i16(imm12)))
355    }
356}
357
358#[cfg(test)]
359mod test {
360    use super::*;
361    #[test]
362    fn test_imm12() {
363        let x = Imm12::ZERO;
364        assert_eq!(0, x.bits());
365        Imm12::maybe_from_u64(0xffff_ffff_ffff_ffff).unwrap();
366    }
367
368    #[test]
369    fn imm20_and_imm12() {
370        assert!(Inst::imm_max() == (i32::MAX - 2048) as i64);
371        assert!(Inst::imm_min() == i32::MIN as i64 - 2048);
372    }
373}