Skip to main content

cranelift_codegen/isa/aarch64/inst/
imms.rs

1//! AArch64 ISA definitions: immediate constants.
2
3#![expect(missing_docs, reason = "fields mostly self-describing")]
4
5use crate::ir::types::*;
6use crate::isa::aarch64::inst::{OperandSize, ScalarSize};
7use crate::machinst::PrettyPrint;
8
9use alloc::string::String;
10
11/// An immediate that represents the NZCV flags.
12#[derive(Clone, Copy, Debug)]
13pub struct NZCV {
14    /// The negative condition flag.
15    n: bool,
16    /// The zero condition flag.
17    z: bool,
18    /// The carry condition flag.
19    c: bool,
20    /// The overflow condition flag.
21    v: bool,
22}
23
24impl NZCV {
25    /// Create a new NZCV flags representation.
26    pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV {
27        NZCV { n, z, c, v }
28    }
29
30    /// Bits for encoding.
31    pub fn bits(&self) -> u32 {
32        (u32::from(self.n) << 3)
33            | (u32::from(self.z) << 2)
34            | (u32::from(self.c) << 1)
35            | u32::from(self.v)
36    }
37}
38
39/// An unsigned 5-bit immediate.
40#[derive(Clone, Copy, Debug)]
41pub struct UImm5 {
42    /// The value.
43    value: u8,
44}
45
46impl UImm5 {
47    /// Create an unsigned 5-bit immediate from u8.
48    pub fn maybe_from_u8(value: u8) -> Option<UImm5> {
49        if value < 32 {
50            Some(UImm5 { value })
51        } else {
52            None
53        }
54    }
55
56    /// Bits for encoding.
57    pub fn bits(&self) -> u32 {
58        u32::from(self.value)
59    }
60}
61
62/// A signed, scaled 7-bit offset.
63#[derive(Clone, Copy, Debug)]
64pub struct SImm7Scaled {
65    /// The value.
66    pub value: i16,
67    /// multiplied by the size of this type
68    pub scale_ty: Type,
69}
70
71impl SImm7Scaled {
72    /// Create a SImm7Scaled from a raw offset and the known scale type, if
73    /// possible.
74    pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled> {
75        assert!(scale_ty == I64 || scale_ty == I32 || scale_ty == F64 || scale_ty == I8X16);
76        let scale = scale_ty.bytes();
77        assert!(scale.is_power_of_two());
78        let scale = i64::from(scale);
79        let upper_limit = 63 * scale;
80        let lower_limit = -(64 * scale);
81        if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 {
82            Some(SImm7Scaled {
83                value: i16::try_from(value).unwrap(),
84                scale_ty,
85            })
86        } else {
87            None
88        }
89    }
90
91    /// Bits for encoding.
92    pub fn bits(&self) -> u32 {
93        let ty_bytes: i16 = self.scale_ty.bytes() as i16;
94        let scaled: i16 = self.value / ty_bytes;
95        assert!(scaled <= 63 && scaled >= -64);
96        let scaled: i8 = scaled as i8;
97        let encoded: u32 = scaled as u32;
98        encoded & 0x7f
99    }
100}
101
102/// Floating-point unit immediate left shift.
103#[derive(Clone, Copy, Debug)]
104pub struct FPULeftShiftImm {
105    /// Shift amount.
106    pub amount: u8,
107    /// Lane size in bits.
108    pub lane_size_in_bits: u8,
109}
110
111impl FPULeftShiftImm {
112    /// Create a floating-point unit immediate left shift from u8.
113    pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
114        debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
115        if amount < lane_size_in_bits {
116            Some(Self {
117                amount,
118                lane_size_in_bits,
119            })
120        } else {
121            None
122        }
123    }
124
125    /// Returns the encoding of the immediate.
126    pub fn enc(&self) -> u32 {
127        debug_assert!(self.lane_size_in_bits.is_power_of_two());
128        debug_assert!(self.lane_size_in_bits > self.amount);
129        // The encoding of the immediate follows the table below,
130        // where xs encode the shift amount.
131        //
132        // | lane_size_in_bits | encoding |
133        // +------------------------------+
134        // | 8                 | 0001xxx  |
135        // | 16                | 001xxxx  |
136        // | 32                | 01xxxxx  |
137        // | 64                | 1xxxxxx  |
138        //
139        // The highest one bit is represented by `lane_size_in_bits`. Since
140        // `lane_size_in_bits` is a power of 2 and `amount` is less
141        // than `lane_size_in_bits`, they can be ORed
142        // together to produced the encoded value.
143        u32::from(self.lane_size_in_bits | self.amount)
144    }
145}
146
147/// Floating-point unit immediate right shift.
148#[derive(Clone, Copy, Debug)]
149pub struct FPURightShiftImm {
150    /// Shift amount.
151    pub amount: u8,
152    /// Lane size in bits.
153    pub lane_size_in_bits: u8,
154}
155
156impl FPURightShiftImm {
157    /// Create a floating-point unit immediate right shift from u8.
158    pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
159        debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
160        if amount > 0 && amount <= lane_size_in_bits {
161            Some(Self {
162                amount,
163                lane_size_in_bits,
164            })
165        } else {
166            None
167        }
168    }
169
170    /// Returns encoding of the immediate.
171    pub fn enc(&self) -> u32 {
172        debug_assert_ne!(0, self.amount);
173        // The encoding of the immediate follows the table below,
174        // where xs encodes the negated shift amount.
175        //
176        // | lane_size_in_bits | encoding |
177        // +------------------------------+
178        // | 8                 | 0001xxx  |
179        // | 16                | 001xxxx  |
180        // | 32                | 01xxxxx  |
181        // | 64                | 1xxxxxx  |
182        //
183        // The shift amount is negated such that a shift amount
184        // of 1 (in 64-bit) is encoded as 0b111111 and a shift
185        // amount of 64 is encoded as 0b000000,
186        // in the bottom 6 bits.
187        u32::from((self.lane_size_in_bits * 2) - self.amount)
188    }
189}
190
191/// a 9-bit signed offset.
192#[derive(Clone, Copy, Debug)]
193pub struct SImm9 {
194    /// The value.
195    pub value: i16,
196}
197
198impl SImm9 {
199    /// Create a signed 9-bit offset from a full-range value, if possible.
200    pub fn maybe_from_i64(value: i64) -> Option<SImm9> {
201        if value >= -256 && value <= 255 {
202            Some(SImm9 {
203                value: value as i16,
204            })
205        } else {
206            None
207        }
208    }
209
210    /// Bits for encoding.
211    pub fn bits(&self) -> u32 {
212        (self.value as u32) & 0x1ff
213    }
214
215    /// Signed value of immediate.
216    pub fn value(&self) -> i32 {
217        self.value as i32
218    }
219}
220
221/// An unsigned, scaled 12-bit offset.
222#[derive(Clone, Copy, Debug)]
223pub struct UImm12Scaled {
224    /// The value.
225    value: u16,
226    /// multiplied by the size of this type
227    scale_ty: Type,
228}
229
230impl UImm12Scaled {
231    /// Create a UImm12Scaled from a raw offset and the known scale type, if
232    /// possible.
233    pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled> {
234        let scale = scale_ty.bytes();
235        assert!(scale.is_power_of_two());
236        let scale = scale as i64;
237        let limit = 4095 * scale;
238        if value >= 0 && value <= limit && (value & (scale - 1)) == 0 {
239            Some(UImm12Scaled {
240                value: value as u16,
241                scale_ty,
242            })
243        } else {
244            None
245        }
246    }
247
248    /// Create a zero immediate of this format.
249    pub fn zero(scale_ty: Type) -> UImm12Scaled {
250        UImm12Scaled { value: 0, scale_ty }
251    }
252
253    /// Encoded bits.
254    pub fn bits(&self) -> u32 {
255        (self.value as u32 / self.scale_ty.bytes()) & 0xfff
256    }
257
258    /// Value after scaling.
259    pub fn value(&self) -> u32 {
260        self.value as u32
261    }
262}
263
264/// A shifted immediate value in 'imm12' format: supports 12 bits, shifted
265/// left by 0 or 12 places.
266#[derive(Copy, Clone, Debug)]
267pub struct Imm12 {
268    /// The immediate bits.
269    pub bits: u16,
270    /// Whether the immediate bits are shifted left by 12 or not.
271    pub shift12: bool,
272}
273
274impl Imm12 {
275    /// Handy 0-value constant.
276    pub const ZERO: Imm12 = Imm12 {
277        bits: 0,
278        shift12: false,
279    };
280
281    /// Compute a Imm12 from raw bits, if possible.
282    pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
283        if val & !0xfff == 0 {
284            Some(Imm12 {
285                bits: val as u16,
286                shift12: false,
287            })
288        } else if val & !(0xfff << 12) == 0 {
289            Some(Imm12 {
290                bits: (val >> 12) as u16,
291                shift12: true,
292            })
293        } else {
294            None
295        }
296    }
297
298    /// Bits for 2-bit "shift" field in e.g. AddI.
299    pub fn shift_bits(&self) -> u32 {
300        if self.shift12 { 0b01 } else { 0b00 }
301    }
302
303    /// Bits for 12-bit "imm" field in e.g. AddI.
304    pub fn imm_bits(&self) -> u32 {
305        self.bits as u32
306    }
307
308    /// Get the actual value that this immediate corresponds to.
309    pub fn value(&self) -> u32 {
310        let base = self.bits as u32;
311        if self.shift12 { base << 12 } else { base }
312    }
313}
314
315/// An immediate for logical instructions.
316#[derive(Copy, Clone, Debug, PartialEq)]
317pub struct ImmLogic {
318    /// The actual value.
319    value: u64,
320    /// `N` flag.
321    pub n: bool,
322    /// `S` field: element size and element bits.
323    pub r: u8,
324    /// `R` field: rotate amount.
325    pub s: u8,
326    /// Was this constructed for a 32-bit or 64-bit instruction?
327    pub size: OperandSize,
328}
329
330impl ImmLogic {
331    /// Compute an ImmLogic from raw bits, if possible.
332    pub fn maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic> {
333        // Note: This function is a port of VIXL's Assembler::IsImmLogical.
334
335        if ty != I64 && ty != I32 {
336            return None;
337        }
338        let operand_size = OperandSize::from_ty(ty);
339
340        let original_value = value;
341
342        let value = if ty == I32 {
343            // To handle 32-bit logical immediates, the very easiest thing is to repeat
344            // the input value twice to make a 64-bit word. The correct encoding of that
345            // as a logical immediate will also be the correct encoding of the 32-bit
346            // value.
347
348            // Avoid making the assumption that the most-significant 32 bits are zero by
349            // shifting the value left and duplicating it.
350            let value = value << 32;
351            value | value >> 32
352        } else {
353            value
354        };
355
356        // Logical immediates are encoded using parameters n, imm_s and imm_r using
357        // the following table:
358        //
359        //    N   imms    immr    size        S             R
360        //    1  ssssss  rrrrrr    64    UInt(ssssss)  UInt(rrrrrr)
361        //    0  0sssss  xrrrrr    32    UInt(sssss)   UInt(rrrrr)
362        //    0  10ssss  xxrrrr    16    UInt(ssss)    UInt(rrrr)
363        //    0  110sss  xxxrrr     8    UInt(sss)     UInt(rrr)
364        //    0  1110ss  xxxxrr     4    UInt(ss)      UInt(rr)
365        //    0  11110s  xxxxxr     2    UInt(s)       UInt(r)
366        // (s bits must not be all set)
367        //
368        // A pattern is constructed of size bits, where the least significant S+1 bits
369        // are set. The pattern is rotated right by R, and repeated across a 32 or
370        // 64-bit value, depending on destination register width.
371        //
372        // Put another way: the basic format of a logical immediate is a single
373        // contiguous stretch of 1 bits, repeated across the whole word at intervals
374        // given by a power of 2. To identify them quickly, we first locate the
375        // lowest stretch of 1 bits, then the next 1 bit above that; that combination
376        // is different for every logical immediate, so it gives us all the
377        // information we need to identify the only logical immediate that our input
378        // could be, and then we simply check if that's the value we actually have.
379        //
380        // (The rotation parameter does give the possibility of the stretch of 1 bits
381        // going 'round the end' of the word. To deal with that, we observe that in
382        // any situation where that happens the bitwise NOT of the value is also a
383        // valid logical immediate. So we simply invert the input whenever its low bit
384        // is set, and then we know that the rotated case can't arise.)
385        let (value, inverted) = if value & 1 == 1 {
386            (!value, true)
387        } else {
388            (value, false)
389        };
390
391        if value == 0 {
392            return None;
393        }
394
395        // The basic analysis idea: imagine our input word looks like this.
396        //
397        //    0011111000111110001111100011111000111110001111100011111000111110
398        //                                                          c  b    a
399        //                                                          |<--d-->|
400        //
401        // We find the lowest set bit (as an actual power-of-2 value, not its index)
402        // and call it a. Then we add a to our original number, which wipes out the
403        // bottommost stretch of set bits and replaces it with a 1 carried into the
404        // next zero bit. Then we look for the new lowest set bit, which is in
405        // position b, and subtract it, so now our number is just like the original
406        // but with the lowest stretch of set bits completely gone. Now we find the
407        // lowest set bit again, which is position c in the diagram above. Then we'll
408        // measure the distance d between bit positions a and c (using CLZ), and that
409        // tells us that the only valid logical immediate that could possibly be equal
410        // to this number is the one in which a stretch of bits running from a to just
411        // below b is replicated every d bits.
412        fn lowest_set_bit(value: u64) -> u64 {
413            let bit = value.trailing_zeros();
414            1u64.checked_shl(bit).unwrap_or(0)
415        }
416        let a = lowest_set_bit(value);
417        assert_ne!(0, a);
418        let value_plus_a = value.wrapping_add(a);
419        let b = lowest_set_bit(value_plus_a);
420        let value_plus_a_minus_b = value_plus_a - b;
421        let c = lowest_set_bit(value_plus_a_minus_b);
422
423        let (d, clz_a, out_n, mask) = if c != 0 {
424            // The general case, in which there is more than one stretch of set bits.
425            // Compute the repeat distance d, and set up a bitmask covering the basic
426            // unit of repetition (i.e. a word with the bottom d bits set). Also, in all
427            // of these cases the N bit of the output will be zero.
428            let clz_a = a.leading_zeros();
429            let clz_c = c.leading_zeros();
430            let d = clz_a - clz_c;
431            let mask = (1 << d) - 1;
432            (d, clz_a, 0, mask)
433        } else {
434            (64, a.leading_zeros(), 1, u64::MAX)
435        };
436
437        // If the repeat period d is not a power of two, it can't be encoded.
438        if !d.is_power_of_two() {
439            return None;
440        }
441
442        if ((b.wrapping_sub(a)) & !mask) != 0 {
443            // If the bit stretch (b - a) does not fit within the mask derived from the
444            // repeat period, then fail.
445            return None;
446        }
447
448        // The only possible option is b - a repeated every d bits. Now we're going to
449        // actually construct the valid logical immediate derived from that
450        // specification, and see if it equals our original input.
451        //
452        // To repeat a value every d bits, we multiply it by a number of the form
453        // (1 + 2^d + 2^(2d) + ...), i.e. 0x0001000100010001 or similar. These can
454        // be derived using a table lookup on CLZ(d).
455        const MULTIPLIERS: [u64; 6] = [
456            0x0000000000000001,
457            0x0000000100000001,
458            0x0001000100010001,
459            0x0101010101010101,
460            0x1111111111111111,
461            0x5555555555555555,
462        ];
463        let multiplier = MULTIPLIERS[(u64::from(d).leading_zeros() - 57) as usize];
464        let candidate = b.wrapping_sub(a) * multiplier;
465
466        if value != candidate {
467            // The candidate pattern doesn't match our input value, so fail.
468            return None;
469        }
470
471        // We have a match! This is a valid logical immediate, so now we have to
472        // construct the bits and pieces of the instruction encoding that generates
473        // it.
474
475        // Count the set bits in our basic stretch. The special case of clz(0) == -1
476        // makes the answer come out right for stretches that reach the very top of
477        // the word (e.g. numbers like 0xffffc00000000000).
478        let clz_b = if b == 0 {
479            u32::MAX // -1
480        } else {
481            b.leading_zeros()
482        };
483        let s = clz_a.wrapping_sub(clz_b);
484
485        // Decide how many bits to rotate right by, to put the low bit of that basic
486        // stretch in position a.
487        let (s, r) = if inverted {
488            // If we inverted the input right at the start of this function, here's
489            // where we compensate: the number of set bits becomes the number of clear
490            // bits, and the rotation count is based on position b rather than position
491            // a (since b is the location of the 'lowest' 1 bit after inversion).
492            // Need wrapping for when clz_b is u32::MAX (for when b == 0).
493            (d - s, clz_b.wrapping_add(1) & (d - 1))
494        } else {
495            (s, (clz_a + 1) & (d - 1))
496        };
497
498        // Now we're done, except for having to encode the S output in such a way that
499        // it gives both the number of set bits and the length of the repeated
500        // segment. The s field is encoded like this:
501        //
502        //     imms    size        S
503        //    ssssss    64    UInt(ssssss)
504        //    0sssss    32    UInt(sssss)
505        //    10ssss    16    UInt(ssss)
506        //    110sss     8    UInt(sss)
507        //    1110ss     4    UInt(ss)
508        //    11110s     2    UInt(s)
509        //
510        // So we 'or' (2 * -d) with our computed s to form imms.
511        let s = ((d * 2).wrapping_neg() | (s - 1)) & 0x3f;
512        debug_assert!(u8::try_from(r).is_ok());
513        debug_assert!(u8::try_from(s).is_ok());
514        Some(ImmLogic {
515            value: original_value,
516            n: out_n != 0,
517            r: r as u8,
518            s: s as u8,
519            size: operand_size,
520        })
521    }
522
523    /// Returns bits ready for encoding: (N:1, R:6, S:6)
524    pub fn enc_bits(&self) -> u32 {
525        ((self.n as u32) << 12) | ((self.r as u32) << 6) | (self.s as u32)
526    }
527
528    /// Returns the value that this immediate represents.
529    pub fn value(&self) -> u64 {
530        self.value
531    }
532
533    /// Return an immediate for the bitwise-inverted value.
534    pub fn invert(&self) -> ImmLogic {
535        // For every ImmLogical immediate, the inverse can also be encoded.
536        Self::maybe_from_u64(!self.value, self.size.to_ty()).unwrap()
537    }
538}
539
540/// An immediate for shift instructions.
541#[derive(Copy, Clone, Debug)]
542pub struct ImmShift {
543    /// 6-bit shift amount.
544    pub imm: u8,
545}
546
547impl ImmShift {
548    /// Create an ImmShift from raw bits, if possible.
549    pub fn maybe_from_u64(val: u64) -> Option<ImmShift> {
550        (val < 64).then_some(ImmShift { imm: val as u8 })
551    }
552
553    /// Get the immediate value.
554    pub fn value(&self) -> u8 {
555        self.imm
556    }
557}
558
559/// A 6-bit immediate used by the `immr` and `imms` fields of bitfield move instructions.
560#[derive(Copy, Clone, Debug)]
561pub struct UImm6 {
562    /// 6-bit immediate.
563    pub imm: u8,
564}
565
566impl UImm6 {
567    /// Create a UImm6 from raw bits, if possible.
568    pub fn maybe_from_u8(val: u8) -> Option<UImm6> {
569        (val < 64).then_some(UImm6 { imm: val })
570    }
571
572    /// Get the immediate value.
573    pub fn value(&self) -> u8 {
574        self.imm
575    }
576}
577
578/// A 16-bit immediate for a MOVZ instruction, with a {0,16,32,48}-bit shift.
579#[derive(Clone, Copy, Debug)]
580pub struct MoveWideConst {
581    /// The value.
582    pub bits: u16,
583    /// Result is `bits` shifted 16*shift bits to the left.
584    pub shift: u8,
585}
586
587impl MoveWideConst {
588    /// Construct a MoveWideConst from an arbitrary 64-bit constant if possible.
589    pub fn maybe_from_u64(value: u64) -> Option<MoveWideConst> {
590        let mask0 = 0x0000_0000_0000_ffffu64;
591        let mask1 = 0x0000_0000_ffff_0000u64;
592        let mask2 = 0x0000_ffff_0000_0000u64;
593        let mask3 = 0xffff_0000_0000_0000u64;
594
595        if value == (value & mask0) {
596            return Some(MoveWideConst {
597                bits: (value & mask0) as u16,
598                shift: 0,
599            });
600        }
601        if value == (value & mask1) {
602            return Some(MoveWideConst {
603                bits: ((value >> 16) & mask0) as u16,
604                shift: 1,
605            });
606        }
607        if value == (value & mask2) {
608            return Some(MoveWideConst {
609                bits: ((value >> 32) & mask0) as u16,
610                shift: 2,
611            });
612        }
613        if value == (value & mask3) {
614            return Some(MoveWideConst {
615                bits: ((value >> 48) & mask0) as u16,
616                shift: 3,
617            });
618        }
619        None
620    }
621
622    /// Create a `MoveWideConst` from a given shift, if possible.
623    pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> {
624        let shift_enc = shift / 16;
625        if shift_enc > 3 {
626            None
627        } else {
628            Some(MoveWideConst {
629                bits: imm,
630                shift: shift_enc,
631            })
632        }
633    }
634
635    /// Create a zero immediate of this format.
636    pub fn zero() -> MoveWideConst {
637        MoveWideConst { bits: 0, shift: 0 }
638    }
639}
640
641/// Advanced SIMD modified immediate as used by MOVI/MVNI.
642#[derive(Clone, Copy, Debug, PartialEq)]
643pub struct ASIMDMovModImm {
644    pub imm: u8,
645    pub shift: u8,
646    pub is_64bit: bool,
647    pub shift_ones: bool,
648}
649
650impl ASIMDMovModImm {
651    /// Construct an ASIMDMovModImm from an arbitrary 64-bit constant, if possible.
652    /// Note that the bits in `value` outside of the range specified by `size` are
653    /// ignored; for example, in the case of `ScalarSize::Size8` all bits above the
654    /// lowest 8 are ignored.
655    pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm> {
656        match size {
657            ScalarSize::Size8 => Some(ASIMDMovModImm {
658                imm: value as u8,
659                shift: 0,
660                is_64bit: false,
661                shift_ones: false,
662            }),
663            ScalarSize::Size16 => {
664                let value = value as u16;
665
666                if value >> 8 == 0 {
667                    Some(ASIMDMovModImm {
668                        imm: value as u8,
669                        shift: 0,
670                        is_64bit: false,
671                        shift_ones: false,
672                    })
673                } else if value as u8 == 0 {
674                    Some(ASIMDMovModImm {
675                        imm: (value >> 8) as u8,
676                        shift: 8,
677                        is_64bit: false,
678                        shift_ones: false,
679                    })
680                } else {
681                    None
682                }
683            }
684            ScalarSize::Size32 => {
685                let value = value as u32;
686
687                // Value is of the form 0x00MMFFFF.
688                if value & 0xFF00FFFF == 0x0000FFFF {
689                    let imm = (value >> 16) as u8;
690
691                    Some(ASIMDMovModImm {
692                        imm,
693                        shift: 16,
694                        is_64bit: false,
695                        shift_ones: true,
696                    })
697                // Value is of the form 0x0000MMFF.
698                } else if value & 0xFFFF00FF == 0x000000FF {
699                    let imm = (value >> 8) as u8;
700
701                    Some(ASIMDMovModImm {
702                        imm,
703                        shift: 8,
704                        is_64bit: false,
705                        shift_ones: true,
706                    })
707                } else {
708                    // Of the 4 bytes, at most one is non-zero.
709                    for shift in (0..32).step_by(8) {
710                        if value & (0xFF << shift) == value {
711                            return Some(ASIMDMovModImm {
712                                imm: (value >> shift) as u8,
713                                shift,
714                                is_64bit: false,
715                                shift_ones: false,
716                            });
717                        }
718                    }
719
720                    None
721                }
722            }
723            ScalarSize::Size64 => {
724                let mut imm = 0u8;
725
726                // Check if all bytes are either 0 or 0xFF.
727                for i in 0..8 {
728                    let b = (value >> (i * 8)) as u8;
729
730                    if b == 0 || b == 0xFF {
731                        imm |= (b & 1) << i;
732                    } else {
733                        return None;
734                    }
735                }
736
737                Some(ASIMDMovModImm {
738                    imm,
739                    shift: 0,
740                    is_64bit: true,
741                    shift_ones: false,
742                })
743            }
744            _ => None,
745        }
746    }
747
748    /// Create a zero immediate of this format.
749    pub fn zero(size: ScalarSize) -> Self {
750        ASIMDMovModImm {
751            imm: 0,
752            shift: 0,
753            is_64bit: size == ScalarSize::Size64,
754            shift_ones: false,
755        }
756    }
757
758    /// Returns the value that this immediate represents.
759    pub fn value(&self) -> (u8, u32, bool) {
760        (self.imm, self.shift as u32, self.shift_ones)
761    }
762}
763
764/// Advanced SIMD modified immediate as used by the vector variant of FMOV.
765#[derive(Clone, Copy, Debug, PartialEq)]
766pub struct ASIMDFPModImm {
767    pub imm: u8,
768    pub size: ScalarSize,
769}
770
771impl ASIMDFPModImm {
772    /// Construct an ASIMDFPModImm from an arbitrary 64-bit constant, if possible.
773    pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDFPModImm> {
774        // In all cases immediates are encoded as an 8-bit number 0b_abcdefgh;
775        // let `D` be the inverse of the digit `d`.
776        match size {
777            ScalarSize::Size16 => {
778                // In this case the representable immediates are 16-bit numbers of the form
779                // 0b_aBbb_cdef_gh00_0000.
780                let value = value as u16;
781                let b0_5 = (value >> 6) & 0b111111;
782                let b6 = (value >> 6) & (1 << 6);
783                let b7 = (value >> 8) & (1 << 7);
784                let imm = (b0_5 | b6 | b7) as u8;
785
786                if value == Self::value16(imm) {
787                    Some(ASIMDFPModImm { imm, size })
788                } else {
789                    None
790                }
791            }
792            ScalarSize::Size32 => {
793                // In this case the representable immediates are 32-bit numbers of the form
794                // 0b_aBbb_bbbc_defg_h000 shifted to the left by 16.
795                let value = value as u32;
796                let b0_5 = (value >> 19) & 0b111111;
797                let b6 = (value >> 19) & (1 << 6);
798                let b7 = (value >> 24) & (1 << 7);
799                let imm = (b0_5 | b6 | b7) as u8;
800
801                if value == Self::value32(imm) {
802                    Some(ASIMDFPModImm { imm, size })
803                } else {
804                    None
805                }
806            }
807            ScalarSize::Size64 => {
808                // In this case the representable immediates are 64-bit numbers of the form
809                // 0b_aBbb_bbbb_bbcd_efgh shifted to the left by 48.
810                let b0_5 = (value >> 48) & 0b111111;
811                let b6 = (value >> 48) & (1 << 6);
812                let b7 = (value >> 56) & (1 << 7);
813                let imm = (b0_5 | b6 | b7) as u8;
814
815                if value == Self::value64(imm) {
816                    Some(ASIMDFPModImm { imm, size })
817                } else {
818                    None
819                }
820            }
821            _ => None,
822        }
823    }
824
825    /// Returns bits ready for encoding.
826    pub fn enc_bits(&self) -> u8 {
827        self.imm
828    }
829
830    /// Returns the 16-bit value that corresponds to an 8-bit encoding.
831    fn value16(imm: u8) -> u16 {
832        let imm = imm as u16;
833        let b0_5 = imm & 0b111111;
834        let b6 = (imm >> 6) & 1;
835        let b6_inv = b6 ^ 1;
836        let b7 = (imm >> 7) & 1;
837
838        b0_5 << 6 | (b6 * 0b11) << 12 | b6_inv << 14 | b7 << 15
839    }
840
841    /// Returns the 32-bit value that corresponds to an 8-bit encoding.
842    fn value32(imm: u8) -> u32 {
843        let imm = imm as u32;
844        let b0_5 = imm & 0b111111;
845        let b6 = (imm >> 6) & 1;
846        let b6_inv = b6 ^ 1;
847        let b7 = (imm >> 7) & 1;
848
849        b0_5 << 19 | (b6 * 0b11111) << 25 | b6_inv << 30 | b7 << 31
850    }
851
852    /// Returns the 64-bit value that corresponds to an 8-bit encoding.
853    fn value64(imm: u8) -> u64 {
854        let imm = imm as u64;
855        let b0_5 = imm & 0b111111;
856        let b6 = (imm >> 6) & 1;
857        let b6_inv = b6 ^ 1;
858        let b7 = (imm >> 7) & 1;
859
860        b0_5 << 48 | (b6 * 0b11111111) << 54 | b6_inv << 62 | b7 << 63
861    }
862}
863
864impl PrettyPrint for NZCV {
865    fn pretty_print(&self, _: u8) -> String {
866        let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c };
867        format!(
868            "#{}{}{}{}",
869            fmt('n', self.n),
870            fmt('z', self.z),
871            fmt('c', self.c),
872            fmt('v', self.v)
873        )
874    }
875}
876
877impl PrettyPrint for UImm5 {
878    fn pretty_print(&self, _: u8) -> String {
879        format!("#{}", self.value)
880    }
881}
882
883impl PrettyPrint for Imm12 {
884    fn pretty_print(&self, _: u8) -> String {
885        let shift = if self.shift12 { 12 } else { 0 };
886        let value = u32::from(self.bits) << shift;
887        format!("#{value}")
888    }
889}
890
891impl PrettyPrint for SImm7Scaled {
892    fn pretty_print(&self, _: u8) -> String {
893        format!("#{}", self.value)
894    }
895}
896
897impl PrettyPrint for FPULeftShiftImm {
898    fn pretty_print(&self, _: u8) -> String {
899        format!("#{}", self.amount)
900    }
901}
902
903impl PrettyPrint for FPURightShiftImm {
904    fn pretty_print(&self, _: u8) -> String {
905        format!("#{}", self.amount)
906    }
907}
908
909impl PrettyPrint for SImm9 {
910    fn pretty_print(&self, _: u8) -> String {
911        format!("#{}", self.value)
912    }
913}
914
915impl PrettyPrint for UImm12Scaled {
916    fn pretty_print(&self, _: u8) -> String {
917        format!("#{}", self.value)
918    }
919}
920
921impl PrettyPrint for ImmLogic {
922    fn pretty_print(&self, _: u8) -> String {
923        format!("#{}", self.value())
924    }
925}
926
927impl PrettyPrint for ImmShift {
928    fn pretty_print(&self, _: u8) -> String {
929        format!("#{}", self.imm)
930    }
931}
932
933impl PrettyPrint for UImm6 {
934    fn pretty_print(&self, _: u8) -> String {
935        format!("#{}", self.imm)
936    }
937}
938
939impl PrettyPrint for MoveWideConst {
940    fn pretty_print(&self, _: u8) -> String {
941        if self.shift == 0 {
942            format!("#{}", self.bits)
943        } else {
944            format!("#{}, LSL #{}", self.bits, self.shift * 16)
945        }
946    }
947}
948
949impl PrettyPrint for ASIMDMovModImm {
950    fn pretty_print(&self, _: u8) -> String {
951        if self.is_64bit {
952            debug_assert_eq!(self.shift, 0);
953
954            let enc_imm = self.imm as i8;
955            let mut imm = 0u64;
956
957            for i in 0..8 {
958                let b = (enc_imm >> i) & 1;
959
960                imm |= (-b as u8 as u64) << (i * 8);
961            }
962
963            format!("#{imm}")
964        } else if self.shift == 0 {
965            format!("#{}", self.imm)
966        } else {
967            let shift_type = if self.shift_ones { "MSL" } else { "LSL" };
968            format!("#{}, {} #{}", self.imm, shift_type, self.shift)
969        }
970    }
971}
972
973impl PrettyPrint for ASIMDFPModImm {
974    fn pretty_print(&self, _: u8) -> String {
975        match self.size {
976            ScalarSize::Size16 => {
977                // FIXME(#8312): Use `f16` once it is stable.
978                // `value` will always be a normal number. Convert it to a `f32`.
979                let value: u32 = Self::value16(self.imm).into();
980                let sign = (value & 0x8000) << 16;
981                // Adjust the exponent for the difference between the `f16` exponent bias and the
982                // `f32` exponent bias.
983                let exponent = ((value & 0x7c00) + ((127 - 15) << 10)) << 13;
984                let significand = (value & 0x3ff) << 13;
985                format!("#{}", f32::from_bits(sign | exponent | significand))
986            }
987            ScalarSize::Size32 => format!("#{}", f32::from_bits(Self::value32(self.imm))),
988            ScalarSize::Size64 => format!("#{}", f64::from_bits(Self::value64(self.imm))),
989            _ => unreachable!(),
990        }
991    }
992}
993
994#[cfg(test)]
995mod test {
996    use super::*;
997
998    #[test]
999    fn imm_logical_test() {
1000        assert_eq!(None, ImmLogic::maybe_from_u64(0, I64));
1001        assert_eq!(None, ImmLogic::maybe_from_u64(u64::MAX, I64));
1002
1003        assert_eq!(
1004            Some(ImmLogic {
1005                value: 1,
1006                n: true,
1007                r: 0,
1008                s: 0,
1009                size: OperandSize::Size64,
1010            }),
1011            ImmLogic::maybe_from_u64(1, I64)
1012        );
1013
1014        assert_eq!(
1015            Some(ImmLogic {
1016                value: 2,
1017                n: true,
1018                r: 63,
1019                s: 0,
1020                size: OperandSize::Size64,
1021            }),
1022            ImmLogic::maybe_from_u64(2, I64)
1023        );
1024
1025        assert_eq!(None, ImmLogic::maybe_from_u64(5, I64));
1026
1027        assert_eq!(None, ImmLogic::maybe_from_u64(11, I64));
1028
1029        assert_eq!(
1030            Some(ImmLogic {
1031                value: 248,
1032                n: true,
1033                r: 61,
1034                s: 4,
1035                size: OperandSize::Size64,
1036            }),
1037            ImmLogic::maybe_from_u64(248, I64)
1038        );
1039
1040        assert_eq!(None, ImmLogic::maybe_from_u64(249, I64));
1041
1042        assert_eq!(
1043            Some(ImmLogic {
1044                value: 1920,
1045                n: true,
1046                r: 57,
1047                s: 3,
1048                size: OperandSize::Size64,
1049            }),
1050            ImmLogic::maybe_from_u64(1920, I64)
1051        );
1052
1053        assert_eq!(
1054            Some(ImmLogic {
1055                value: 0x7ffe,
1056                n: true,
1057                r: 63,
1058                s: 13,
1059                size: OperandSize::Size64,
1060            }),
1061            ImmLogic::maybe_from_u64(0x7ffe, I64)
1062        );
1063
1064        assert_eq!(
1065            Some(ImmLogic {
1066                value: 0x30000,
1067                n: true,
1068                r: 48,
1069                s: 1,
1070                size: OperandSize::Size64,
1071            }),
1072            ImmLogic::maybe_from_u64(0x30000, I64)
1073        );
1074
1075        assert_eq!(
1076            Some(ImmLogic {
1077                value: 0x100000,
1078                n: true,
1079                r: 44,
1080                s: 0,
1081                size: OperandSize::Size64,
1082            }),
1083            ImmLogic::maybe_from_u64(0x100000, I64)
1084        );
1085
1086        assert_eq!(
1087            Some(ImmLogic {
1088                value: u64::MAX - 1,
1089                n: true,
1090                r: 63,
1091                s: 62,
1092                size: OperandSize::Size64,
1093            }),
1094            ImmLogic::maybe_from_u64(u64::MAX - 1, I64)
1095        );
1096
1097        assert_eq!(
1098            Some(ImmLogic {
1099                value: 0xaaaaaaaaaaaaaaaa,
1100                n: false,
1101                r: 1,
1102                s: 60,
1103                size: OperandSize::Size64,
1104            }),
1105            ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64)
1106        );
1107
1108        assert_eq!(
1109            Some(ImmLogic {
1110                value: 0x8181818181818181,
1111                n: false,
1112                r: 1,
1113                s: 49,
1114                size: OperandSize::Size64,
1115            }),
1116            ImmLogic::maybe_from_u64(0x8181818181818181, I64)
1117        );
1118
1119        assert_eq!(
1120            Some(ImmLogic {
1121                value: 0xffc3ffc3ffc3ffc3,
1122                n: false,
1123                r: 10,
1124                s: 43,
1125                size: OperandSize::Size64,
1126            }),
1127            ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64)
1128        );
1129
1130        assert_eq!(
1131            Some(ImmLogic {
1132                value: 0x100000001,
1133                n: false,
1134                r: 0,
1135                s: 0,
1136                size: OperandSize::Size64,
1137            }),
1138            ImmLogic::maybe_from_u64(0x100000001, I64)
1139        );
1140
1141        assert_eq!(
1142            Some(ImmLogic {
1143                value: 0x1111111111111111,
1144                n: false,
1145                r: 0,
1146                s: 56,
1147                size: OperandSize::Size64,
1148            }),
1149            ImmLogic::maybe_from_u64(0x1111111111111111, I64)
1150        );
1151
1152        for n in 0..2 {
1153            let types = if n == 0 { vec![I64, I32] } else { vec![I64] };
1154            for s in 0..64 {
1155                for r in 0..64 {
1156                    let imm = get_logical_imm(n, s, r);
1157                    for &ty in &types {
1158                        match ImmLogic::maybe_from_u64(imm, ty) {
1159                            Some(ImmLogic { value, .. }) => {
1160                                assert_eq!(imm, value);
1161                                ImmLogic::maybe_from_u64(!value, ty).unwrap();
1162                            }
1163                            None => assert_eq!(0, imm),
1164                        };
1165                    }
1166                }
1167            }
1168        }
1169    }
1170
1171    // Repeat a value that has `width` bits, across a 64-bit value.
1172    fn repeat(value: u64, width: u64) -> u64 {
1173        let mut result = value & ((1 << width) - 1);
1174        let mut i = width;
1175        while i < 64 {
1176            result |= result << i;
1177            i *= 2;
1178        }
1179        result
1180    }
1181
1182    // Get the logical immediate, from the encoding N/R/S bits.
1183    fn get_logical_imm(n: u32, s: u32, r: u32) -> u64 {
1184        // An integer is constructed from the n, imm_s and imm_r bits according to
1185        // the following table:
1186        //
1187        //  N   imms    immr    size        S             R
1188        //  1  ssssss  rrrrrr    64    UInt(ssssss)  UInt(rrrrrr)
1189        //  0  0sssss  xrrrrr    32    UInt(sssss)   UInt(rrrrr)
1190        //  0  10ssss  xxrrrr    16    UInt(ssss)    UInt(rrrr)
1191        //  0  110sss  xxxrrr     8    UInt(sss)     UInt(rrr)
1192        //  0  1110ss  xxxxrr     4    UInt(ss)      UInt(rr)
1193        //  0  11110s  xxxxxr     2    UInt(s)       UInt(r)
1194        // (s bits must not be all set)
1195        //
1196        // A pattern is constructed of size bits, where the least significant S+1
1197        // bits are set. The pattern is rotated right by R, and repeated across a
1198        // 64-bit value.
1199
1200        if n == 1 {
1201            if s == 0x3f {
1202                return 0;
1203            }
1204            let bits = (1u64 << (s + 1)) - 1;
1205            bits.rotate_right(r)
1206        } else {
1207            if (s >> 1) == 0x1f {
1208                return 0;
1209            }
1210            let mut width = 0x20;
1211            while width >= 0x2 {
1212                if (s & width) == 0 {
1213                    let mask = width - 1;
1214                    if (s & mask) == mask {
1215                        return 0;
1216                    }
1217                    let bits = (1u64 << ((s & mask) + 1)) - 1;
1218                    return repeat(bits.rotate_right(r & mask), width.into());
1219                }
1220                width >>= 1;
1221            }
1222            unreachable!();
1223        }
1224    }
1225
1226    #[test]
1227    fn asimd_fp_mod_imm_test() {
1228        assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size32));
1229        assert_eq!(
1230            None,
1231            ASIMDFPModImm::maybe_from_u64(0.013671875_f32.to_bits() as u64, ScalarSize::Size32)
1232        );
1233        assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size64));
1234        assert_eq!(
1235            None,
1236            ASIMDFPModImm::maybe_from_u64(10000_f64.to_bits(), ScalarSize::Size64)
1237        );
1238    }
1239
1240    #[test]
1241    fn asimd_mov_mod_imm_test() {
1242        assert_eq!(
1243            None,
1244            ASIMDMovModImm::maybe_from_u64(513, ScalarSize::Size16)
1245        );
1246        assert_eq!(
1247            None,
1248            ASIMDMovModImm::maybe_from_u64(4278190335, ScalarSize::Size32)
1249        );
1250        assert_eq!(
1251            None,
1252            ASIMDMovModImm::maybe_from_u64(8388608, ScalarSize::Size64)
1253        );
1254
1255        assert_eq!(
1256            Some(ASIMDMovModImm {
1257                imm: 66,
1258                shift: 16,
1259                is_64bit: false,
1260                shift_ones: true,
1261            }),
1262            ASIMDMovModImm::maybe_from_u64(4390911, ScalarSize::Size32)
1263        );
1264    }
1265}