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