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