1 //! AArch64 ISA definitions: immediate constants.
2 
3 use crate::ir::types::*;
4 use crate::isa::aarch64::inst::{OperandSize, ScalarSize};
5 use crate::machinst::PrettyPrint;
6 
7 use alloc::string::String;
8 
9 /// An immediate that represents the NZCV flags.
10 #[derive(Clone, Copy, Debug)]
11 pub 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 
22 impl NZCV {
23     /// Create a new NZCV flags representation.
new(n: bool, z: bool, c: bool, v: bool) -> NZCV24     pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV {
25         NZCV { n, z, c, v }
26     }
27 
28     /// Bits for encoding.
bits(&self) -> u3229     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)]
39 pub struct UImm5 {
40     /// The value.
41     value: u8,
42 }
43 
44 impl UImm5 {
45     /// Create an unsigned 5-bit immediate from u8.
maybe_from_u8(value: u8) -> Option<UImm5>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.
bits(&self) -> u3255     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)]
62 pub struct SImm7Scaled {
63     /// The value.
64     pub value: i16,
65     /// multiplied by the size of this type
66     pub scale_ty: Type,
67 }
68 
69 impl SImm7Scaled {
70     /// Create a SImm7Scaled from a raw offset and the known scale type, if
71     /// possible.
maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled>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.
bits(&self) -> u3290     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)]
102 pub struct FPULeftShiftImm {
103     /// Shift amount.
104     pub amount: u8,
105     /// Lane size in bits.
106     pub lane_size_in_bits: u8,
107 }
108 
109 impl FPULeftShiftImm {
110     /// Create a floating-point unit immediate left shift from u8.
maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self>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.
enc(&self) -> u32124     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)]
147 pub struct FPURightShiftImm {
148     /// Shift amount.
149     pub amount: u8,
150     /// Lane size in bits.
151     pub lane_size_in_bits: u8,
152 }
153 
154 impl FPURightShiftImm {
155     /// Create a floating-point unit immediate right shift from u8.
maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self>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.
enc(&self) -> u32169     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)]
191 pub struct SImm9 {
192     /// The value.
193     pub value: i16,
194 }
195 
196 impl SImm9 {
197     /// Create a signed 9-bit offset from a full-range value, if possible.
maybe_from_i64(value: i64) -> Option<SImm9>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.
bits(&self) -> u32209     pub fn bits(&self) -> u32 {
210         (self.value as u32) & 0x1ff
211     }
212 
213     /// Signed value of immediate.
value(&self) -> i32214     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)]
221 pub struct UImm12Scaled {
222     /// The value.
223     value: u16,
224     /// multiplied by the size of this type
225     scale_ty: Type,
226 }
227 
228 impl UImm12Scaled {
229     /// Create a UImm12Scaled from a raw offset and the known scale type, if
230     /// possible.
maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled>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.
zero(scale_ty: Type) -> UImm12Scaled247     pub fn zero(scale_ty: Type) -> UImm12Scaled {
248         UImm12Scaled { value: 0, scale_ty }
249     }
250 
251     /// Encoded bits.
bits(&self) -> u32252     pub fn bits(&self) -> u32 {
253         (self.value as u32 / self.scale_ty.bytes()) & 0xfff
254     }
255 
256     /// Value after scaling.
value(&self) -> u32257     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)]
265 pub 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 
272 impl 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.
maybe_from_u64(val: u64) -> Option<Imm12>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.
shift_bits(&self) -> u32297     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.
imm_bits(&self) -> u32302     pub fn imm_bits(&self) -> u32 {
303         self.bits as u32
304     }
305 
306     /// Get the actual value that this immediate corresponds to.
value(&self) -> u32307     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)]
315 pub 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 
328 impl ImmLogic {
329     /// Compute an ImmLogic from raw bits, if possible.
maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic>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)
enc_bits(&self) -> u32522     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.
value(&self) -> u64527     pub fn value(&self) -> u64 {
528         self.value
529     }
530 
531     /// Return an immediate for the bitwise-inverted value.
invert(&self) -> ImmLogic532     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)]
540 pub struct ImmShift {
541     /// 6-bit shift amount.
542     pub imm: u8,
543 }
544 
545 impl ImmShift {
546     /// Create an ImmShift from raw bits, if possible.
maybe_from_u64(val: u64) -> Option<ImmShift>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.
value(&self) -> u8556     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)]
563 pub 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 
570 impl MoveWideConst {
571     /// Construct a MoveWideConst from an arbitrary 64-bit constant if possible.
maybe_from_u64(value: u64) -> Option<MoveWideConst>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 `MoveWideConst` from a given shift, if possible.
maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst>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.
zero() -> MoveWideConst619     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)]
626 pub struct ASIMDMovModImm {
627     imm: u8,
628     shift: u8,
629     is_64bit: bool,
630     shift_ones: bool,
631 }
632 
633 impl 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.
maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm>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.
zero(size: ScalarSize) -> Self732     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.
value(&self) -> (u8, u32, bool)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)]
749 pub struct ASIMDFPModImm {
750     imm: u8,
751     size: ScalarSize,
752 }
753 
754 impl ASIMDFPModImm {
755     /// Construct an ASIMDFPModImm from an arbitrary 64-bit constant, if possible.
maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDFPModImm>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.
enc_bits(&self) -> u8809     pub fn enc_bits(&self) -> u8 {
810         self.imm
811     }
812 
813     /// Returns the 16-bit value that corresponds to an 8-bit encoding.
value16(imm: u8) -> u16814     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.
value32(imm: u8) -> u32825     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.
value64(imm: u8) -> u64836     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 
847 impl PrettyPrint for NZCV {
pretty_print(&self, _: u8) -> String848     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 
860 impl PrettyPrint for UImm5 {
pretty_print(&self, _: u8) -> String861     fn pretty_print(&self, _: u8) -> String {
862         format!("#{}", self.value)
863     }
864 }
865 
866 impl PrettyPrint for Imm12 {
pretty_print(&self, _: u8) -> String867     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 
874 impl PrettyPrint for SImm7Scaled {
pretty_print(&self, _: u8) -> String875     fn pretty_print(&self, _: u8) -> String {
876         format!("#{}", self.value)
877     }
878 }
879 
880 impl PrettyPrint for FPULeftShiftImm {
pretty_print(&self, _: u8) -> String881     fn pretty_print(&self, _: u8) -> String {
882         format!("#{}", self.amount)
883     }
884 }
885 
886 impl PrettyPrint for FPURightShiftImm {
pretty_print(&self, _: u8) -> String887     fn pretty_print(&self, _: u8) -> String {
888         format!("#{}", self.amount)
889     }
890 }
891 
892 impl PrettyPrint for SImm9 {
pretty_print(&self, _: u8) -> String893     fn pretty_print(&self, _: u8) -> String {
894         format!("#{}", self.value)
895     }
896 }
897 
898 impl PrettyPrint for UImm12Scaled {
pretty_print(&self, _: u8) -> String899     fn pretty_print(&self, _: u8) -> String {
900         format!("#{}", self.value)
901     }
902 }
903 
904 impl PrettyPrint for ImmLogic {
pretty_print(&self, _: u8) -> String905     fn pretty_print(&self, _: u8) -> String {
906         format!("#{}", self.value())
907     }
908 }
909 
910 impl PrettyPrint for ImmShift {
pretty_print(&self, _: u8) -> String911     fn pretty_print(&self, _: u8) -> String {
912         format!("#{}", self.imm)
913     }
914 }
915 
916 impl PrettyPrint for MoveWideConst {
pretty_print(&self, _: u8) -> String917     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 
926 impl PrettyPrint for ASIMDMovModImm {
pretty_print(&self, _: u8) -> String927     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 
950 impl PrettyPrint for ASIMDFPModImm {
pretty_print(&self, _: u8) -> String951     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)]
972 mod test {
973     use super::*;
974 
975     #[test]
imm_logical_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.
repeat(value: u64, width: u64) -> u641149     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.
get_logical_imm(n: u32, s: u32, r: u32) -> u641160     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]
asimd_fp_mod_imm_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]
asimd_mov_mod_imm_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 }
1243