cranelift_codegen/isa/aarch64/inst/
imms.rs

1//! AArch64 ISA definitions: immediate constants.
2
3use crate::ir::types::*;
4use crate::isa::aarch64::inst::{OperandSize, ScalarSize};
5use crate::machinst::PrettyPrint;
6
7use std::string::String;
8
9/// An immediate that represents the NZCV flags.
10#[derive(Clone, Copy, Debug)]
11pub struct NZCV {
12    /// The negative condition flag.
13    n: bool,
14    /// The zero condition flag.
15    z: bool,
16    /// The carry condition flag.
17    c: bool,
18    /// The overflow condition flag.
19    v: bool,
20}
21
22impl NZCV {
23    /// Create a new NZCV flags representation.
24    pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV {
25        NZCV { n, z, c, v }
26    }
27
28    /// Bits for encoding.
29    pub fn bits(&self) -> u32 {
30        (u32::from(self.n) << 3)
31            | (u32::from(self.z) << 2)
32            | (u32::from(self.c) << 1)
33            | u32::from(self.v)
34    }
35}
36
37/// An unsigned 5-bit immediate.
38#[derive(Clone, Copy, Debug)]
39pub struct UImm5 {
40    /// The value.
41    value: u8,
42}
43
44impl UImm5 {
45    /// Create an unsigned 5-bit immediate from u8.
46    pub fn maybe_from_u8(value: u8) -> Option<UImm5> {
47        if value < 32 {
48            Some(UImm5 { value })
49        } else {
50            None
51        }
52    }
53
54    /// Bits for encoding.
55    pub fn bits(&self) -> u32 {
56        u32::from(self.value)
57    }
58}
59
60/// A signed, scaled 7-bit offset.
61#[derive(Clone, Copy, Debug)]
62pub struct SImm7Scaled {
63    /// The value.
64    pub value: i16,
65    /// multiplied by the size of this type
66    pub scale_ty: Type,
67}
68
69impl SImm7Scaled {
70    /// Create a SImm7Scaled from a raw offset and the known scale type, if
71    /// possible.
72    pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled> {
73        assert!(scale_ty == I64 || scale_ty == I32 || scale_ty == F64 || scale_ty == I8X16);
74        let scale = scale_ty.bytes();
75        assert!(scale.is_power_of_two());
76        let scale = i64::from(scale);
77        let upper_limit = 63 * scale;
78        let lower_limit = -(64 * scale);
79        if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 {
80            Some(SImm7Scaled {
81                value: i16::try_from(value).unwrap(),
82                scale_ty,
83            })
84        } else {
85            None
86        }
87    }
88
89    /// Bits for encoding.
90    pub fn bits(&self) -> u32 {
91        let ty_bytes: i16 = self.scale_ty.bytes() as i16;
92        let scaled: i16 = self.value / ty_bytes;
93        assert!(scaled <= 63 && scaled >= -64);
94        let scaled: i8 = scaled as i8;
95        let encoded: u32 = scaled as u32;
96        encoded & 0x7f
97    }
98}
99
100/// Floating-point unit immediate left shift.
101#[derive(Clone, Copy, Debug)]
102pub struct FPULeftShiftImm {
103    /// Shift amount.
104    pub amount: u8,
105    /// Lane size in bits.
106    pub lane_size_in_bits: u8,
107}
108
109impl FPULeftShiftImm {
110    /// Create a floating-point unit immediate left shift from u8.
111    pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
112        debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
113        if amount < lane_size_in_bits {
114            Some(Self {
115                amount,
116                lane_size_in_bits,
117            })
118        } else {
119            None
120        }
121    }
122
123    /// Returns the encoding of the immediate.
124    pub fn enc(&self) -> u32 {
125        debug_assert!(self.lane_size_in_bits.is_power_of_two());
126        debug_assert!(self.lane_size_in_bits > self.amount);
127        // The encoding of the immediate follows the table below,
128        // where xs encode the shift amount.
129        //
130        // | lane_size_in_bits | encoding |
131        // +------------------------------+
132        // | 8                 | 0001xxx  |
133        // | 16                | 001xxxx  |
134        // | 32                | 01xxxxx  |
135        // | 64                | 1xxxxxx  |
136        //
137        // The highest one bit is represented by `lane_size_in_bits`. Since
138        // `lane_size_in_bits` is a power of 2 and `amount` is less
139        // than `lane_size_in_bits`, they can be ORed
140        // together to produced the encoded value.
141        u32::from(self.lane_size_in_bits | self.amount)
142    }
143}
144
145/// Floating-point unit immediate right shift.
146#[derive(Clone, Copy, Debug)]
147pub struct FPURightShiftImm {
148    /// Shift amount.
149    pub amount: u8,
150    /// Lane size in bits.
151    pub lane_size_in_bits: u8,
152}
153
154impl FPURightShiftImm {
155    /// Create a floating-point unit immediate right shift from u8.
156    pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
157        debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
158        if amount > 0 && amount <= lane_size_in_bits {
159            Some(Self {
160                amount,
161                lane_size_in_bits,
162            })
163        } else {
164            None
165        }
166    }
167
168    /// Returns encoding of the immediate.
169    pub fn enc(&self) -> u32 {
170        debug_assert_ne!(0, self.amount);
171        // The encoding of the immediate follows the table below,
172        // where xs encodes the negated shift amount.
173        //
174        // | lane_size_in_bits | encoding |
175        // +------------------------------+
176        // | 8                 | 0001xxx  |
177        // | 16                | 001xxxx  |
178        // | 32                | 01xxxxx  |
179        // | 64                | 1xxxxxx  |
180        //
181        // The shift amount is negated such that a shift amount
182        // of 1 (in 64-bit) is encoded as 0b111111 and a shift
183        // amount of 64 is encoded as 0b000000,
184        // in the bottom 6 bits.
185        u32::from((self.lane_size_in_bits * 2) - self.amount)
186    }
187}
188
189/// a 9-bit signed offset.
190#[derive(Clone, Copy, Debug)]
191pub struct SImm9 {
192    /// The value.
193    pub value: i16,
194}
195
196impl SImm9 {
197    /// Create a signed 9-bit offset from a full-range value, if possible.
198    pub fn maybe_from_i64(value: i64) -> Option<SImm9> {
199        if value >= -256 && value <= 255 {
200            Some(SImm9 {
201                value: value as i16,
202            })
203        } else {
204            None
205        }
206    }
207
208    /// Bits for encoding.
209    pub fn bits(&self) -> u32 {
210        (self.value as u32) & 0x1ff
211    }
212
213    /// Signed value of immediate.
214    pub fn value(&self) -> i32 {
215        self.value as i32
216    }
217}
218
219/// An unsigned, scaled 12-bit offset.
220#[derive(Clone, Copy, Debug)]
221pub struct UImm12Scaled {
222    /// The value.
223    value: u16,
224    /// multiplied by the size of this type
225    scale_ty: Type,
226}
227
228impl UImm12Scaled {
229    /// Create a UImm12Scaled from a raw offset and the known scale type, if
230    /// possible.
231    pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled> {
232        let scale = scale_ty.bytes();
233        assert!(scale.is_power_of_two());
234        let scale = scale as i64;
235        let limit = 4095 * scale;
236        if value >= 0 && value <= limit && (value & (scale - 1)) == 0 {
237            Some(UImm12Scaled {
238                value: value as u16,
239                scale_ty,
240            })
241        } else {
242            None
243        }
244    }
245
246    /// Create a zero immediate of this format.
247    pub fn zero(scale_ty: Type) -> UImm12Scaled {
248        UImm12Scaled { value: 0, scale_ty }
249    }
250
251    /// Encoded bits.
252    pub fn bits(&self) -> u32 {
253        (self.value as u32 / self.scale_ty.bytes()) & 0xfff
254    }
255
256    /// Value after scaling.
257    pub fn value(&self) -> u32 {
258        self.value as u32
259    }
260}
261
262/// A shifted immediate value in 'imm12' format: supports 12 bits, shifted
263/// left by 0 or 12 places.
264#[derive(Copy, Clone, Debug)]
265pub struct Imm12 {
266    /// The immediate bits.
267    pub bits: u16,
268    /// Whether the immediate bits are shifted left by 12 or not.
269    pub shift12: bool,
270}
271
272impl Imm12 {
273    /// Compute a Imm12 from raw bits, if possible.
274    pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
275        if val & !0xfff == 0 {
276            Some(Imm12 {
277                bits: val as u16,
278                shift12: false,
279            })
280        } else if val & !(0xfff << 12) == 0 {
281            Some(Imm12 {
282                bits: (val >> 12) as u16,
283                shift12: true,
284            })
285        } else {
286            None
287        }
288    }
289
290    /// Bits for 2-bit "shift" field in e.g. AddI.
291    pub fn shift_bits(&self) -> u32 {
292        if self.shift12 {
293            0b01
294        } else {
295            0b00
296        }
297    }
298
299    /// Bits for 12-bit "imm" field in e.g. AddI.
300    pub fn imm_bits(&self) -> u32 {
301        self.bits as u32
302    }
303
304    /// Get the actual value that this immediate corresponds to.
305    pub fn value(&self) -> u32 {
306        let base = self.bits as u32;
307        if self.shift12 {
308            base << 12
309        } else {
310            base
311        }
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_value())
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_value() // -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 max_value() (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        if val < 64 {
551            Some(ImmShift { imm: val as u8 })
552        } else {
553            None
554        }
555    }
556
557    /// Get the immediate value.
558    pub fn value(&self) -> u8 {
559        self.imm
560    }
561}
562
563/// A 16-bit immediate for a MOVZ instruction, with a {0,16,32,48}-bit shift.
564#[derive(Clone, Copy, Debug)]
565pub struct MoveWideConst {
566    /// The value.
567    pub bits: u16,
568    /// Result is `bits` shifted 16*shift bits to the left.
569    pub shift: u8,
570}
571
572impl MoveWideConst {
573    /// Construct a MoveWideConst from an arbitrary 64-bit constant if possible.
574    pub fn maybe_from_u64(value: u64) -> Option<MoveWideConst> {
575        let mask0 = 0x0000_0000_0000_ffffu64;
576        let mask1 = 0x0000_0000_ffff_0000u64;
577        let mask2 = 0x0000_ffff_0000_0000u64;
578        let mask3 = 0xffff_0000_0000_0000u64;
579
580        if value == (value & mask0) {
581            return Some(MoveWideConst {
582                bits: (value & mask0) as u16,
583                shift: 0,
584            });
585        }
586        if value == (value & mask1) {
587            return Some(MoveWideConst {
588                bits: ((value >> 16) & mask0) as u16,
589                shift: 1,
590            });
591        }
592        if value == (value & mask2) {
593            return Some(MoveWideConst {
594                bits: ((value >> 32) & mask0) as u16,
595                shift: 2,
596            });
597        }
598        if value == (value & mask3) {
599            return Some(MoveWideConst {
600                bits: ((value >> 48) & mask0) as u16,
601                shift: 3,
602            });
603        }
604        None
605    }
606
607    /// Create a `MoveWideCosnt` from a given shift, if possible.
608    pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> {
609        let shift_enc = shift / 16;
610        if shift_enc > 3 {
611            None
612        } else {
613            Some(MoveWideConst {
614                bits: imm,
615                shift: shift_enc,
616            })
617        }
618    }
619
620    /// Create a zero immediate of this format.
621    pub fn zero() -> MoveWideConst {
622        MoveWideConst { bits: 0, shift: 0 }
623    }
624}
625
626/// Advanced SIMD modified immediate as used by MOVI/MVNI.
627#[derive(Clone, Copy, Debug, PartialEq)]
628pub struct ASIMDMovModImm {
629    imm: u8,
630    shift: u8,
631    is_64bit: bool,
632    shift_ones: bool,
633}
634
635impl ASIMDMovModImm {
636    /// Construct an ASIMDMovModImm from an arbitrary 64-bit constant, if possible.
637    /// Note that the bits in `value` outside of the range specified by `size` are
638    /// ignored; for example, in the case of `ScalarSize::Size8` all bits above the
639    /// lowest 8 are ignored.
640    pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm> {
641        match size {
642            ScalarSize::Size8 => Some(ASIMDMovModImm {
643                imm: value as u8,
644                shift: 0,
645                is_64bit: false,
646                shift_ones: false,
647            }),
648            ScalarSize::Size16 => {
649                let value = value as u16;
650
651                if value >> 8 == 0 {
652                    Some(ASIMDMovModImm {
653                        imm: value as u8,
654                        shift: 0,
655                        is_64bit: false,
656                        shift_ones: false,
657                    })
658                } else if value as u8 == 0 {
659                    Some(ASIMDMovModImm {
660                        imm: (value >> 8) as u8,
661                        shift: 8,
662                        is_64bit: false,
663                        shift_ones: false,
664                    })
665                } else {
666                    None
667                }
668            }
669            ScalarSize::Size32 => {
670                let value = value as u32;
671
672                // Value is of the form 0x00MMFFFF.
673                if value & 0xFF00FFFF == 0x0000FFFF {
674                    let imm = (value >> 16) as u8;
675
676                    Some(ASIMDMovModImm {
677                        imm,
678                        shift: 16,
679                        is_64bit: false,
680                        shift_ones: true,
681                    })
682                // Value is of the form 0x0000MMFF.
683                } else if value & 0xFFFF00FF == 0x000000FF {
684                    let imm = (value >> 8) as u8;
685
686                    Some(ASIMDMovModImm {
687                        imm,
688                        shift: 8,
689                        is_64bit: false,
690                        shift_ones: true,
691                    })
692                } else {
693                    // Of the 4 bytes, at most one is non-zero.
694                    for shift in (0..32).step_by(8) {
695                        if value & (0xFF << shift) == value {
696                            return Some(ASIMDMovModImm {
697                                imm: (value >> shift) as u8,
698                                shift,
699                                is_64bit: false,
700                                shift_ones: false,
701                            });
702                        }
703                    }
704
705                    None
706                }
707            }
708            ScalarSize::Size64 => {
709                let mut imm = 0u8;
710
711                // Check if all bytes are either 0 or 0xFF.
712                for i in 0..8 {
713                    let b = (value >> (i * 8)) as u8;
714
715                    if b == 0 || b == 0xFF {
716                        imm |= (b & 1) << i;
717                    } else {
718                        return None;
719                    }
720                }
721
722                Some(ASIMDMovModImm {
723                    imm,
724                    shift: 0,
725                    is_64bit: true,
726                    shift_ones: false,
727                })
728            }
729            _ => None,
730        }
731    }
732
733    /// Create a zero immediate of this format.
734    pub fn zero(size: ScalarSize) -> Self {
735        ASIMDMovModImm {
736            imm: 0,
737            shift: 0,
738            is_64bit: size == ScalarSize::Size64,
739            shift_ones: false,
740        }
741    }
742
743    /// Returns the value that this immediate represents.
744    pub fn value(&self) -> (u8, u32, bool) {
745        (self.imm, self.shift as u32, self.shift_ones)
746    }
747}
748
749/// Advanced SIMD modified immediate as used by the vector variant of FMOV.
750#[derive(Clone, Copy, Debug, PartialEq)]
751pub struct ASIMDFPModImm {
752    imm: u8,
753    size: ScalarSize,
754}
755
756impl ASIMDFPModImm {
757    /// Construct an ASIMDFPModImm from an arbitrary 64-bit constant, if possible.
758    pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDFPModImm> {
759        // In all cases immediates are encoded as an 8-bit number 0b_abcdefgh;
760        // let `D` be the inverse of the digit `d`.
761        match size {
762            ScalarSize::Size16 => {
763                // In this case the representable immediates are 16-bit numbers of the form
764                // 0b_aBbb_cdef_gh00_0000.
765                let value = value as u16;
766                let b0_5 = (value >> 6) & 0b111111;
767                let b6 = (value >> 6) & (1 << 6);
768                let b7 = (value >> 8) & (1 << 7);
769                let imm = (b0_5 | b6 | b7) as u8;
770
771                if value == Self::value16(imm) {
772                    Some(ASIMDFPModImm { imm, size })
773                } else {
774                    None
775                }
776            }
777            ScalarSize::Size32 => {
778                // In this case the representable immediates are 32-bit numbers of the form
779                // 0b_aBbb_bbbc_defg_h000 shifted to the left by 16.
780                let value = value as u32;
781                let b0_5 = (value >> 19) & 0b111111;
782                let b6 = (value >> 19) & (1 << 6);
783                let b7 = (value >> 24) & (1 << 7);
784                let imm = (b0_5 | b6 | b7) as u8;
785
786                if value == Self::value32(imm) {
787                    Some(ASIMDFPModImm { imm, size })
788                } else {
789                    None
790                }
791            }
792            ScalarSize::Size64 => {
793                // In this case the representable immediates are 64-bit numbers of the form
794                // 0b_aBbb_bbbb_bbcd_efgh shifted to the left by 48.
795                let b0_5 = (value >> 48) & 0b111111;
796                let b6 = (value >> 48) & (1 << 6);
797                let b7 = (value >> 56) & (1 << 7);
798                let imm = (b0_5 | b6 | b7) as u8;
799
800                if value == Self::value64(imm) {
801                    Some(ASIMDFPModImm { imm, size })
802                } else {
803                    None
804                }
805            }
806            _ => None,
807        }
808    }
809
810    /// Returns bits ready for encoding.
811    pub fn enc_bits(&self) -> u8 {
812        self.imm
813    }
814
815    /// Returns the 16-bit value that corresponds to an 8-bit encoding.
816    fn value16(imm: u8) -> u16 {
817        let imm = imm as u16;
818        let b0_5 = imm & 0b111111;
819        let b6 = (imm >> 6) & 1;
820        let b6_inv = b6 ^ 1;
821        let b7 = (imm >> 7) & 1;
822
823        b0_5 << 6 | (b6 * 0b11) << 12 | b6_inv << 14 | b7 << 15
824    }
825
826    /// Returns the 32-bit value that corresponds to an 8-bit encoding.
827    fn value32(imm: u8) -> u32 {
828        let imm = imm as u32;
829        let b0_5 = imm & 0b111111;
830        let b6 = (imm >> 6) & 1;
831        let b6_inv = b6 ^ 1;
832        let b7 = (imm >> 7) & 1;
833
834        b0_5 << 19 | (b6 * 0b11111) << 25 | b6_inv << 30 | b7 << 31
835    }
836
837    /// Returns the 64-bit value that corresponds to an 8-bit encoding.
838    fn value64(imm: u8) -> u64 {
839        let imm = imm as u64;
840        let b0_5 = imm & 0b111111;
841        let b6 = (imm >> 6) & 1;
842        let b6_inv = b6 ^ 1;
843        let b7 = (imm >> 7) & 1;
844
845        b0_5 << 48 | (b6 * 0b11111111) << 54 | b6_inv << 62 | b7 << 63
846    }
847}
848
849impl PrettyPrint for NZCV {
850    fn pretty_print(&self, _: u8) -> String {
851        let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c };
852        format!(
853            "#{}{}{}{}",
854            fmt('n', self.n),
855            fmt('z', self.z),
856            fmt('c', self.c),
857            fmt('v', self.v)
858        )
859    }
860}
861
862impl PrettyPrint for UImm5 {
863    fn pretty_print(&self, _: u8) -> String {
864        format!("#{}", self.value)
865    }
866}
867
868impl PrettyPrint for Imm12 {
869    fn pretty_print(&self, _: u8) -> String {
870        let shift = if self.shift12 { 12 } else { 0 };
871        let value = u32::from(self.bits) << shift;
872        format!("#{value}")
873    }
874}
875
876impl PrettyPrint for SImm7Scaled {
877    fn pretty_print(&self, _: u8) -> String {
878        format!("#{}", self.value)
879    }
880}
881
882impl PrettyPrint for FPULeftShiftImm {
883    fn pretty_print(&self, _: u8) -> String {
884        format!("#{}", self.amount)
885    }
886}
887
888impl PrettyPrint for FPURightShiftImm {
889    fn pretty_print(&self, _: u8) -> String {
890        format!("#{}", self.amount)
891    }
892}
893
894impl PrettyPrint for SImm9 {
895    fn pretty_print(&self, _: u8) -> String {
896        format!("#{}", self.value)
897    }
898}
899
900impl PrettyPrint for UImm12Scaled {
901    fn pretty_print(&self, _: u8) -> String {
902        format!("#{}", self.value)
903    }
904}
905
906impl PrettyPrint for ImmLogic {
907    fn pretty_print(&self, _: u8) -> String {
908        format!("#{}", self.value())
909    }
910}
911
912impl PrettyPrint for ImmShift {
913    fn pretty_print(&self, _: u8) -> String {
914        format!("#{}", self.imm)
915    }
916}
917
918impl PrettyPrint for MoveWideConst {
919    fn pretty_print(&self, _: u8) -> String {
920        if self.shift == 0 {
921            format!("#{}", self.bits)
922        } else {
923            format!("#{}, LSL #{}", self.bits, self.shift * 16)
924        }
925    }
926}
927
928impl PrettyPrint for ASIMDMovModImm {
929    fn pretty_print(&self, _: u8) -> String {
930        if self.is_64bit {
931            debug_assert_eq!(self.shift, 0);
932
933            let enc_imm = self.imm as i8;
934            let mut imm = 0u64;
935
936            for i in 0..8 {
937                let b = (enc_imm >> i) & 1;
938
939                imm |= (-b as u8 as u64) << (i * 8);
940            }
941
942            format!("#{imm}")
943        } else if self.shift == 0 {
944            format!("#{}", self.imm)
945        } else {
946            let shift_type = if self.shift_ones { "MSL" } else { "LSL" };
947            format!("#{}, {} #{}", self.imm, shift_type, self.shift)
948        }
949    }
950}
951
952impl PrettyPrint for ASIMDFPModImm {
953    fn pretty_print(&self, _: u8) -> String {
954        match self.size {
955            ScalarSize::Size16 => {
956                // FIXME(#8312): Use `f16` once it is stable.
957                // `value` will always be a normal number. Convert it to a `f32`.
958                let value: u32 = Self::value16(self.imm).into();
959                let sign = (value & 0x8000) << 16;
960                // Adjust the exponent for the difference between the `f16` exponent bias and the
961                // `f32` exponent bias.
962                let exponent = ((value & 0x7c00) + ((127 - 15) << 10)) << 13;
963                let significand = (value & 0x3ff) << 13;
964                format!("#{}", f32::from_bits(sign | exponent | significand))
965            }
966            ScalarSize::Size32 => format!("#{}", f32::from_bits(Self::value32(self.imm))),
967            ScalarSize::Size64 => format!("#{}", f64::from_bits(Self::value64(self.imm))),
968            _ => unreachable!(),
969        }
970    }
971}
972
973#[cfg(test)]
974mod test {
975    use super::*;
976
977    #[test]
978    fn imm_logical_test() {
979        assert_eq!(None, ImmLogic::maybe_from_u64(0, I64));
980        assert_eq!(None, ImmLogic::maybe_from_u64(u64::max_value(), I64));
981
982        assert_eq!(
983            Some(ImmLogic {
984                value: 1,
985                n: true,
986                r: 0,
987                s: 0,
988                size: OperandSize::Size64,
989            }),
990            ImmLogic::maybe_from_u64(1, I64)
991        );
992
993        assert_eq!(
994            Some(ImmLogic {
995                value: 2,
996                n: true,
997                r: 63,
998                s: 0,
999                size: OperandSize::Size64,
1000            }),
1001            ImmLogic::maybe_from_u64(2, I64)
1002        );
1003
1004        assert_eq!(None, ImmLogic::maybe_from_u64(5, I64));
1005
1006        assert_eq!(None, ImmLogic::maybe_from_u64(11, I64));
1007
1008        assert_eq!(
1009            Some(ImmLogic {
1010                value: 248,
1011                n: true,
1012                r: 61,
1013                s: 4,
1014                size: OperandSize::Size64,
1015            }),
1016            ImmLogic::maybe_from_u64(248, I64)
1017        );
1018
1019        assert_eq!(None, ImmLogic::maybe_from_u64(249, I64));
1020
1021        assert_eq!(
1022            Some(ImmLogic {
1023                value: 1920,
1024                n: true,
1025                r: 57,
1026                s: 3,
1027                size: OperandSize::Size64,
1028            }),
1029            ImmLogic::maybe_from_u64(1920, I64)
1030        );
1031
1032        assert_eq!(
1033            Some(ImmLogic {
1034                value: 0x7ffe,
1035                n: true,
1036                r: 63,
1037                s: 13,
1038                size: OperandSize::Size64,
1039            }),
1040            ImmLogic::maybe_from_u64(0x7ffe, I64)
1041        );
1042
1043        assert_eq!(
1044            Some(ImmLogic {
1045                value: 0x30000,
1046                n: true,
1047                r: 48,
1048                s: 1,
1049                size: OperandSize::Size64,
1050            }),
1051            ImmLogic::maybe_from_u64(0x30000, I64)
1052        );
1053
1054        assert_eq!(
1055            Some(ImmLogic {
1056                value: 0x100000,
1057                n: true,
1058                r: 44,
1059                s: 0,
1060                size: OperandSize::Size64,
1061            }),
1062            ImmLogic::maybe_from_u64(0x100000, I64)
1063        );
1064
1065        assert_eq!(
1066            Some(ImmLogic {
1067                value: u64::max_value() - 1,
1068                n: true,
1069                r: 63,
1070                s: 62,
1071                size: OperandSize::Size64,
1072            }),
1073            ImmLogic::maybe_from_u64(u64::max_value() - 1, I64)
1074        );
1075
1076        assert_eq!(
1077            Some(ImmLogic {
1078                value: 0xaaaaaaaaaaaaaaaa,
1079                n: false,
1080                r: 1,
1081                s: 60,
1082                size: OperandSize::Size64,
1083            }),
1084            ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64)
1085        );
1086
1087        assert_eq!(
1088            Some(ImmLogic {
1089                value: 0x8181818181818181,
1090                n: false,
1091                r: 1,
1092                s: 49,
1093                size: OperandSize::Size64,
1094            }),
1095            ImmLogic::maybe_from_u64(0x8181818181818181, I64)
1096        );
1097
1098        assert_eq!(
1099            Some(ImmLogic {
1100                value: 0xffc3ffc3ffc3ffc3,
1101                n: false,
1102                r: 10,
1103                s: 43,
1104                size: OperandSize::Size64,
1105            }),
1106            ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64)
1107        );
1108
1109        assert_eq!(
1110            Some(ImmLogic {
1111                value: 0x100000001,
1112                n: false,
1113                r: 0,
1114                s: 0,
1115                size: OperandSize::Size64,
1116            }),
1117            ImmLogic::maybe_from_u64(0x100000001, I64)
1118        );
1119
1120        assert_eq!(
1121            Some(ImmLogic {
1122                value: 0x1111111111111111,
1123                n: false,
1124                r: 0,
1125                s: 56,
1126                size: OperandSize::Size64,
1127            }),
1128            ImmLogic::maybe_from_u64(0x1111111111111111, I64)
1129        );
1130
1131        for n in 0..2 {
1132            let types = if n == 0 { vec![I64, I32] } else { vec![I64] };
1133            for s in 0..64 {
1134                for r in 0..64 {
1135                    let imm = get_logical_imm(n, s, r);
1136                    for &ty in &types {
1137                        match ImmLogic::maybe_from_u64(imm, ty) {
1138                            Some(ImmLogic { value, .. }) => {
1139                                assert_eq!(imm, value);
1140                                ImmLogic::maybe_from_u64(!value, ty).unwrap();
1141                            }
1142                            None => assert_eq!(0, imm),
1143                        };
1144                    }
1145                }
1146            }
1147        }
1148    }
1149
1150    // Repeat a value that has `width` bits, across a 64-bit value.
1151    fn repeat(value: u64, width: u64) -> u64 {
1152        let mut result = value & ((1 << width) - 1);
1153        let mut i = width;
1154        while i < 64 {
1155            result |= result << i;
1156            i *= 2;
1157        }
1158        result
1159    }
1160
1161    // Get the logical immediate, from the encoding N/R/S bits.
1162    fn get_logical_imm(n: u32, s: u32, r: u32) -> u64 {
1163        // An integer is constructed from the n, imm_s and imm_r bits according to
1164        // the following table:
1165        //
1166        //  N   imms    immr    size        S             R
1167        //  1  ssssss  rrrrrr    64    UInt(ssssss)  UInt(rrrrrr)
1168        //  0  0sssss  xrrrrr    32    UInt(sssss)   UInt(rrrrr)
1169        //  0  10ssss  xxrrrr    16    UInt(ssss)    UInt(rrrr)
1170        //  0  110sss  xxxrrr     8    UInt(sss)     UInt(rrr)
1171        //  0  1110ss  xxxxrr     4    UInt(ss)      UInt(rr)
1172        //  0  11110s  xxxxxr     2    UInt(s)       UInt(r)
1173        // (s bits must not be all set)
1174        //
1175        // A pattern is constructed of size bits, where the least significant S+1
1176        // bits are set. The pattern is rotated right by R, and repeated across a
1177        // 64-bit value.
1178
1179        if n == 1 {
1180            if s == 0x3f {
1181                return 0;
1182            }
1183            let bits = (1u64 << (s + 1)) - 1;
1184            bits.rotate_right(r)
1185        } else {
1186            if (s >> 1) == 0x1f {
1187                return 0;
1188            }
1189            let mut width = 0x20;
1190            while width >= 0x2 {
1191                if (s & width) == 0 {
1192                    let mask = width - 1;
1193                    if (s & mask) == mask {
1194                        return 0;
1195                    }
1196                    let bits = (1u64 << ((s & mask) + 1)) - 1;
1197                    return repeat(bits.rotate_right(r & mask), width.into());
1198                }
1199                width >>= 1;
1200            }
1201            unreachable!();
1202        }
1203    }
1204
1205    #[test]
1206    fn asimd_fp_mod_imm_test() {
1207        assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size32));
1208        assert_eq!(
1209            None,
1210            ASIMDFPModImm::maybe_from_u64(0.013671875_f32.to_bits() as u64, ScalarSize::Size32)
1211        );
1212        assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size64));
1213        assert_eq!(
1214            None,
1215            ASIMDFPModImm::maybe_from_u64(10000_f64.to_bits(), ScalarSize::Size64)
1216        );
1217    }
1218
1219    #[test]
1220    fn asimd_mov_mod_imm_test() {
1221        assert_eq!(
1222            None,
1223            ASIMDMovModImm::maybe_from_u64(513, ScalarSize::Size16)
1224        );
1225        assert_eq!(
1226            None,
1227            ASIMDMovModImm::maybe_from_u64(4278190335, ScalarSize::Size32)
1228        );
1229        assert_eq!(
1230            None,
1231            ASIMDMovModImm::maybe_from_u64(8388608, ScalarSize::Size64)
1232        );
1233
1234        assert_eq!(
1235            Some(ASIMDMovModImm {
1236                imm: 66,
1237                shift: 16,
1238                is_64bit: false,
1239                shift_ones: true,
1240            }),
1241            ASIMDMovModImm::maybe_from_u64(4390911, ScalarSize::Size32)
1242        );
1243    }
1244}