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::InstSize;
8 use crate::machinst::*;
9 
10 use regalloc::RealRegUniverse;
11 
12 use core::convert::TryFrom;
13 use std::string::String;
14 
15 /// An immediate that represents the NZCV flags.
16 #[derive(Clone, Copy, Debug)]
17 pub struct NZCV {
18     /// The negative condition flag.
19     n: bool,
20     /// The zero condition flag.
21     z: bool,
22     /// The carry condition flag.
23     c: bool,
24     /// The overflow condition flag.
25     v: bool,
26 }
27 
28 impl NZCV {
new(n: bool, z: bool, c: bool, v: bool) -> NZCV29     pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV {
30         NZCV { n, z, c, v }
31     }
32 
33     /// Bits for encoding.
bits(&self) -> u3234     pub fn bits(&self) -> u32 {
35         (u32::from(self.n) << 3)
36             | (u32::from(self.z) << 2)
37             | (u32::from(self.c) << 1)
38             | u32::from(self.v)
39     }
40 }
41 
42 /// An unsigned 5-bit immediate.
43 #[derive(Clone, Copy, Debug)]
44 pub struct UImm5 {
45     /// The value.
46     value: u8,
47 }
48 
49 impl UImm5 {
maybe_from_u8(value: u8) -> Option<UImm5>50     pub fn maybe_from_u8(value: u8) -> Option<UImm5> {
51         if value < 32 {
52             Some(UImm5 { value })
53         } else {
54             None
55         }
56     }
57 
58     /// Bits for encoding.
bits(&self) -> u3259     pub fn bits(&self) -> u32 {
60         u32::from(self.value)
61     }
62 }
63 
64 /// A signed, scaled 7-bit offset.
65 #[derive(Clone, Copy, Debug)]
66 pub struct SImm7Scaled {
67     /// The value.
68     pub value: i16,
69     /// multiplied by the size of this type
70     pub scale_ty: Type,
71 }
72 
73 impl SImm7Scaled {
74     /// Create a SImm7Scaled from a raw offset and the known scale type, if
75     /// possible.
maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled>76     pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled> {
77         assert!(scale_ty == I64 || scale_ty == I32);
78         let scale = scale_ty.bytes();
79         assert!(scale.is_power_of_two());
80         let scale = i64::from(scale);
81         let upper_limit = 63 * scale;
82         let lower_limit = -(64 * scale);
83         if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 {
84             Some(SImm7Scaled {
85                 value: i16::try_from(value).unwrap(),
86                 scale_ty,
87             })
88         } else {
89             None
90         }
91     }
92 
93     /// Create a zero immediate of this format.
zero(scale_ty: Type) -> SImm7Scaled94     pub fn zero(scale_ty: Type) -> SImm7Scaled {
95         SImm7Scaled { value: 0, scale_ty }
96     }
97 
98     /// Bits for encoding.
bits(&self) -> u3299     pub fn bits(&self) -> u32 {
100         let ty_bytes: i16 = self.scale_ty.bytes() as i16;
101         let scaled: i16 = self.value / ty_bytes;
102         assert!(scaled <= 63 && scaled >= -64);
103         let scaled: i8 = scaled as i8;
104         let encoded: u32 = scaled as u32;
105         encoded & 0x7f
106     }
107 }
108 
109 /// a 9-bit signed offset.
110 #[derive(Clone, Copy, Debug)]
111 pub struct SImm9 {
112     /// The value.
113     pub value: i16,
114 }
115 
116 impl SImm9 {
117     /// Create a signed 9-bit offset from a full-range value, if possible.
maybe_from_i64(value: i64) -> Option<SImm9>118     pub fn maybe_from_i64(value: i64) -> Option<SImm9> {
119         if value >= -256 && value <= 255 {
120             Some(SImm9 {
121                 value: value as i16,
122             })
123         } else {
124             None
125         }
126     }
127 
128     /// Create a zero immediate of this format.
zero() -> SImm9129     pub fn zero() -> SImm9 {
130         SImm9 { value: 0 }
131     }
132 
133     /// Bits for encoding.
bits(&self) -> u32134     pub fn bits(&self) -> u32 {
135         (self.value as u32) & 0x1ff
136     }
137 }
138 
139 /// An unsigned, scaled 12-bit offset.
140 #[derive(Clone, Copy, Debug)]
141 pub struct UImm12Scaled {
142     /// The value.
143     pub value: u16,
144     /// multiplied by the size of this type
145     pub scale_ty: Type,
146 }
147 
148 impl UImm12Scaled {
149     /// Create a UImm12Scaled from a raw offset and the known scale type, if
150     /// possible.
maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled>151     pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled> {
152         let scale = scale_ty.bytes();
153         assert!(scale.is_power_of_two());
154         let scale = scale as i64;
155         let limit = 4095 * scale;
156         if value >= 0 && value <= limit && (value & (scale - 1)) == 0 {
157             Some(UImm12Scaled {
158                 value: value as u16,
159                 scale_ty,
160             })
161         } else {
162             None
163         }
164     }
165 
166     /// Create a zero immediate of this format.
zero(scale_ty: Type) -> UImm12Scaled167     pub fn zero(scale_ty: Type) -> UImm12Scaled {
168         UImm12Scaled { value: 0, scale_ty }
169     }
170 
171     /// Encoded bits.
bits(&self) -> u32172     pub fn bits(&self) -> u32 {
173         (self.value as u32 / self.scale_ty.bytes()) & 0xfff
174     }
175 }
176 
177 /// A shifted immediate value in 'imm12' format: supports 12 bits, shifted
178 /// left by 0 or 12 places.
179 #[derive(Clone, Debug)]
180 pub struct Imm12 {
181     /// The immediate bits.
182     pub bits: u16,
183     /// Whether the immediate bits are shifted left by 12 or not.
184     pub shift12: bool,
185 }
186 
187 impl Imm12 {
188     /// Compute a Imm12 from raw bits, if possible.
maybe_from_u64(val: u64) -> Option<Imm12>189     pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
190         if val == 0 {
191             Some(Imm12 {
192                 bits: 0,
193                 shift12: false,
194             })
195         } else if val < 0xfff {
196             Some(Imm12 {
197                 bits: val as u16,
198                 shift12: false,
199             })
200         } else if val < 0xfff_000 && (val & 0xfff == 0) {
201             Some(Imm12 {
202                 bits: (val >> 12) as u16,
203                 shift12: true,
204             })
205         } else {
206             None
207         }
208     }
209 
210     /// Bits for 2-bit "shift" field in e.g. AddI.
shift_bits(&self) -> u32211     pub fn shift_bits(&self) -> u32 {
212         if self.shift12 {
213             0b01
214         } else {
215             0b00
216         }
217     }
218 
219     /// Bits for 12-bit "imm" field in e.g. AddI.
imm_bits(&self) -> u32220     pub fn imm_bits(&self) -> u32 {
221         self.bits as u32
222     }
223 }
224 
225 /// An immediate for logical instructions.
226 #[derive(Clone, Debug)]
227 #[cfg_attr(test, derive(PartialEq))]
228 pub struct ImmLogic {
229     /// The actual value.
230     value: u64,
231     /// `N` flag.
232     pub n: bool,
233     /// `S` field: element size and element bits.
234     pub r: u8,
235     /// `R` field: rotate amount.
236     pub s: u8,
237     /// Was this constructed for a 32-bit or 64-bit instruction?
238     pub size: InstSize,
239 }
240 
241 impl ImmLogic {
242     /// Compute an ImmLogic from raw bits, if possible.
maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic>243     pub fn maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic> {
244         // Note: This function is a port of VIXL's Assembler::IsImmLogical.
245 
246         if ty != I64 && ty != I32 {
247             return None;
248         }
249         let inst_size = InstSize::from_ty(ty);
250 
251         let original_value = value;
252 
253         let value = if ty == I32 {
254             // To handle 32-bit logical immediates, the very easiest thing is to repeat
255             // the input value twice to make a 64-bit word. The correct encoding of that
256             // as a logical immediate will also be the correct encoding of the 32-bit
257             // value.
258 
259             // Avoid making the assumption that the most-significant 32 bits are zero by
260             // shifting the value left and duplicating it.
261             let value = value << 32;
262             value | value >> 32
263         } else {
264             value
265         };
266 
267         // Logical immediates are encoded using parameters n, imm_s and imm_r using
268         // the following table:
269         //
270         //    N   imms    immr    size        S             R
271         //    1  ssssss  rrrrrr    64    UInt(ssssss)  UInt(rrrrrr)
272         //    0  0sssss  xrrrrr    32    UInt(sssss)   UInt(rrrrr)
273         //    0  10ssss  xxrrrr    16    UInt(ssss)    UInt(rrrr)
274         //    0  110sss  xxxrrr     8    UInt(sss)     UInt(rrr)
275         //    0  1110ss  xxxxrr     4    UInt(ss)      UInt(rr)
276         //    0  11110s  xxxxxr     2    UInt(s)       UInt(r)
277         // (s bits must not be all set)
278         //
279         // A pattern is constructed of size bits, where the least significant S+1 bits
280         // are set. The pattern is rotated right by R, and repeated across a 32 or
281         // 64-bit value, depending on destination register width.
282         //
283         // Put another way: the basic format of a logical immediate is a single
284         // contiguous stretch of 1 bits, repeated across the whole word at intervals
285         // given by a power of 2. To identify them quickly, we first locate the
286         // lowest stretch of 1 bits, then the next 1 bit above that; that combination
287         // is different for every logical immediate, so it gives us all the
288         // information we need to identify the only logical immediate that our input
289         // could be, and then we simply check if that's the value we actually have.
290         //
291         // (The rotation parameter does give the possibility of the stretch of 1 bits
292         // going 'round the end' of the word. To deal with that, we observe that in
293         // any situation where that happens the bitwise NOT of the value is also a
294         // valid logical immediate. So we simply invert the input whenever its low bit
295         // is set, and then we know that the rotated case can't arise.)
296         let (value, inverted) = if value & 1 == 1 {
297             (!value, true)
298         } else {
299             (value, false)
300         };
301 
302         if value == 0 {
303             return None;
304         }
305 
306         // The basic analysis idea: imagine our input word looks like this.
307         //
308         //    0011111000111110001111100011111000111110001111100011111000111110
309         //                                                          c  b    a
310         //                                                          |<--d-->|
311         //
312         // We find the lowest set bit (as an actual power-of-2 value, not its index)
313         // and call it a. Then we add a to our original number, which wipes out the
314         // bottommost stretch of set bits and replaces it with a 1 carried into the
315         // next zero bit. Then we look for the new lowest set bit, which is in
316         // position b, and subtract it, so now our number is just like the original
317         // but with the lowest stretch of set bits completely gone. Now we find the
318         // lowest set bit again, which is position c in the diagram above. Then we'll
319         // measure the distance d between bit positions a and c (using CLZ), and that
320         // tells us that the only valid logical immediate that could possibly be equal
321         // to this number is the one in which a stretch of bits running from a to just
322         // below b is replicated every d bits.
323         fn lowest_set_bit(value: u64) -> u64 {
324             let bit = value.trailing_zeros();
325             1u64.checked_shl(bit).unwrap_or(0)
326         }
327         let a = lowest_set_bit(value);
328         assert_ne!(0, a);
329         let value_plus_a = value.wrapping_add(a);
330         let b = lowest_set_bit(value_plus_a);
331         let value_plus_a_minus_b = value_plus_a - b;
332         let c = lowest_set_bit(value_plus_a_minus_b);
333 
334         let (d, clz_a, out_n, mask) = if c != 0 {
335             // The general case, in which there is more than one stretch of set bits.
336             // Compute the repeat distance d, and set up a bitmask covering the basic
337             // unit of repetition (i.e. a word with the bottom d bits set). Also, in all
338             // of these cases the N bit of the output will be zero.
339             let clz_a = a.leading_zeros();
340             let clz_c = c.leading_zeros();
341             let d = clz_a - clz_c;
342             let mask = (1 << d) - 1;
343             (d, clz_a, 0, mask)
344         } else {
345             (64, a.leading_zeros(), 1, u64::max_value())
346         };
347 
348         // If the repeat period d is not a power of two, it can't be encoded.
349         if !d.is_power_of_two() {
350             return None;
351         }
352 
353         if ((b.wrapping_sub(a)) & !mask) != 0 {
354             // If the bit stretch (b - a) does not fit within the mask derived from the
355             // repeat period, then fail.
356             return None;
357         }
358 
359         // The only possible option is b - a repeated every d bits. Now we're going to
360         // actually construct the valid logical immediate derived from that
361         // specification, and see if it equals our original input.
362         //
363         // To repeat a value every d bits, we multiply it by a number of the form
364         // (1 + 2^d + 2^(2d) + ...), i.e. 0x0001000100010001 or similar. These can
365         // be derived using a table lookup on CLZ(d).
366         const MULTIPLIERS: [u64; 6] = [
367             0x0000000000000001,
368             0x0000000100000001,
369             0x0001000100010001,
370             0x0101010101010101,
371             0x1111111111111111,
372             0x5555555555555555,
373         ];
374         let multiplier = MULTIPLIERS[(u64::from(d).leading_zeros() - 57) as usize];
375         let candidate = b.wrapping_sub(a) * multiplier;
376 
377         if value != candidate {
378             // The candidate pattern doesn't match our input value, so fail.
379             return None;
380         }
381 
382         // We have a match! This is a valid logical immediate, so now we have to
383         // construct the bits and pieces of the instruction encoding that generates
384         // it.
385 
386         // Count the set bits in our basic stretch. The special case of clz(0) == -1
387         // makes the answer come out right for stretches that reach the very top of
388         // the word (e.g. numbers like 0xffffc00000000000).
389         let clz_b = if b == 0 {
390             u32::max_value() // -1
391         } else {
392             b.leading_zeros()
393         };
394         let s = clz_a.wrapping_sub(clz_b);
395 
396         // Decide how many bits to rotate right by, to put the low bit of that basic
397         // stretch in position a.
398         let (s, r) = if inverted {
399             // If we inverted the input right at the start of this function, here's
400             // where we compensate: the number of set bits becomes the number of clear
401             // bits, and the rotation count is based on position b rather than position
402             // a (since b is the location of the 'lowest' 1 bit after inversion).
403             // Need wrapping for when clz_b is max_value() (for when b == 0).
404             (d - s, clz_b.wrapping_add(1) & (d - 1))
405         } else {
406             (s, (clz_a + 1) & (d - 1))
407         };
408 
409         // Now we're done, except for having to encode the S output in such a way that
410         // it gives both the number of set bits and the length of the repeated
411         // segment. The s field is encoded like this:
412         //
413         //     imms    size        S
414         //    ssssss    64    UInt(ssssss)
415         //    0sssss    32    UInt(sssss)
416         //    10ssss    16    UInt(ssss)
417         //    110sss     8    UInt(sss)
418         //    1110ss     4    UInt(ss)
419         //    11110s     2    UInt(s)
420         //
421         // So we 'or' (2 * -d) with our computed s to form imms.
422         let s = ((d * 2).wrapping_neg() | (s - 1)) & 0x3f;
423         debug_assert!(u8::try_from(r).is_ok());
424         debug_assert!(u8::try_from(s).is_ok());
425         Some(ImmLogic {
426             value: original_value,
427             n: out_n != 0,
428             r: r as u8,
429             s: s as u8,
430             size: inst_size,
431         })
432     }
433 
434     /// Returns bits ready for encoding: (N:1, R:6, S:6)
enc_bits(&self) -> u32435     pub fn enc_bits(&self) -> u32 {
436         ((self.n as u32) << 12) | ((self.r as u32) << 6) | (self.s as u32)
437     }
438 
439     /// Returns the value that this immediate represents.
value(&self) -> u64440     pub fn value(&self) -> u64 {
441         self.value
442     }
443 
444     /// Return an immediate for the bitwise-inverted value.
invert(&self) -> ImmLogic445     pub fn invert(&self) -> ImmLogic {
446         // For every ImmLogical immediate, the inverse can also be encoded.
447         Self::maybe_from_u64(!self.value, self.size.to_ty()).unwrap()
448     }
449 }
450 
451 /// An immediate for shift instructions.
452 #[derive(Clone, Debug)]
453 pub struct ImmShift {
454     /// 6-bit shift amount.
455     pub imm: u8,
456 }
457 
458 impl ImmShift {
459     /// Create an ImmShift from raw bits, if possible.
maybe_from_u64(val: u64) -> Option<ImmShift>460     pub fn maybe_from_u64(val: u64) -> Option<ImmShift> {
461         if val < 64 {
462             Some(ImmShift { imm: val as u8 })
463         } else {
464             None
465         }
466     }
467 
468     /// Get the immediate value.
value(&self) -> u8469     pub fn value(&self) -> u8 {
470         self.imm
471     }
472 }
473 
474 /// A 16-bit immediate for a MOVZ instruction, with a {0,16,32,48}-bit shift.
475 #[derive(Clone, Copy, Debug)]
476 pub struct MoveWideConst {
477     /// The value.
478     pub bits: u16,
479     /// Result is `bits` shifted 16*shift bits to the left.
480     pub shift: u8,
481 }
482 
483 impl MoveWideConst {
484     /// Construct a MoveWideConst from an arbitrary 64-bit constant if possible.
maybe_from_u64(value: u64) -> Option<MoveWideConst>485     pub fn maybe_from_u64(value: u64) -> Option<MoveWideConst> {
486         let mask0 = 0x0000_0000_0000_ffffu64;
487         let mask1 = 0x0000_0000_ffff_0000u64;
488         let mask2 = 0x0000_ffff_0000_0000u64;
489         let mask3 = 0xffff_0000_0000_0000u64;
490 
491         if value == (value & mask0) {
492             return Some(MoveWideConst {
493                 bits: (value & mask0) as u16,
494                 shift: 0,
495             });
496         }
497         if value == (value & mask1) {
498             return Some(MoveWideConst {
499                 bits: ((value >> 16) & mask0) as u16,
500                 shift: 1,
501             });
502         }
503         if value == (value & mask2) {
504             return Some(MoveWideConst {
505                 bits: ((value >> 32) & mask0) as u16,
506                 shift: 2,
507             });
508         }
509         if value == (value & mask3) {
510             return Some(MoveWideConst {
511                 bits: ((value >> 48) & mask0) as u16,
512                 shift: 3,
513             });
514         }
515         None
516     }
517 
maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst>518     pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> {
519         let shift_enc = shift / 16;
520         if shift_enc > 3 {
521             None
522         } else {
523             Some(MoveWideConst {
524                 bits: imm,
525                 shift: shift_enc,
526             })
527         }
528     }
529 
530     /// Returns the value that this constant represents.
value(&self) -> u64531     pub fn value(&self) -> u64 {
532         (self.bits as u64) << (16 * self.shift)
533     }
534 }
535 
536 impl ShowWithRRU for NZCV {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String537     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
538         let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c };
539         format!(
540             "#{}{}{}{}",
541             fmt('n', self.n),
542             fmt('z', self.z),
543             fmt('c', self.c),
544             fmt('v', self.v)
545         )
546     }
547 }
548 
549 impl ShowWithRRU for UImm5 {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String550     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
551         format!("#{}", self.value)
552     }
553 }
554 
555 impl ShowWithRRU for Imm12 {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String556     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
557         let shift = if self.shift12 { 12 } else { 0 };
558         let value = u32::from(self.bits) << shift;
559         format!("#{}", value)
560     }
561 }
562 
563 impl ShowWithRRU for SImm7Scaled {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String564     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
565         format!("#{}", self.value)
566     }
567 }
568 
569 impl ShowWithRRU for SImm9 {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String570     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
571         format!("#{}", self.value)
572     }
573 }
574 
575 impl ShowWithRRU for UImm12Scaled {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String576     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
577         format!("#{}", self.value)
578     }
579 }
580 
581 impl ShowWithRRU for ImmLogic {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String582     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
583         format!("#{}", self.value())
584     }
585 }
586 
587 impl ShowWithRRU for ImmShift {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String588     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
589         format!("#{}", self.imm)
590     }
591 }
592 
593 impl ShowWithRRU for MoveWideConst {
show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String594     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
595         if self.shift == 0 {
596             format!("#{}", self.bits)
597         } else {
598             format!("#{}, LSL #{}", self.bits, self.shift * 16)
599         }
600     }
601 }
602 
603 #[cfg(test)]
604 mod test {
605     use super::*;
606 
607     #[test]
imm_logical_test()608     fn imm_logical_test() {
609         assert_eq!(None, ImmLogic::maybe_from_u64(0, I64));
610         assert_eq!(None, ImmLogic::maybe_from_u64(u64::max_value(), I64));
611 
612         assert_eq!(
613             Some(ImmLogic {
614                 value: 1,
615                 n: true,
616                 r: 0,
617                 s: 0,
618                 size: InstSize::Size64,
619             }),
620             ImmLogic::maybe_from_u64(1, I64)
621         );
622 
623         assert_eq!(
624             Some(ImmLogic {
625                 value: 2,
626                 n: true,
627                 r: 63,
628                 s: 0,
629                 size: InstSize::Size64,
630             }),
631             ImmLogic::maybe_from_u64(2, I64)
632         );
633 
634         assert_eq!(None, ImmLogic::maybe_from_u64(5, I64));
635 
636         assert_eq!(None, ImmLogic::maybe_from_u64(11, I64));
637 
638         assert_eq!(
639             Some(ImmLogic {
640                 value: 248,
641                 n: true,
642                 r: 61,
643                 s: 4,
644                 size: InstSize::Size64,
645             }),
646             ImmLogic::maybe_from_u64(248, I64)
647         );
648 
649         assert_eq!(None, ImmLogic::maybe_from_u64(249, I64));
650 
651         assert_eq!(
652             Some(ImmLogic {
653                 value: 1920,
654                 n: true,
655                 r: 57,
656                 s: 3,
657                 size: InstSize::Size64,
658             }),
659             ImmLogic::maybe_from_u64(1920, I64)
660         );
661 
662         assert_eq!(
663             Some(ImmLogic {
664                 value: 0x7ffe,
665                 n: true,
666                 r: 63,
667                 s: 13,
668                 size: InstSize::Size64,
669             }),
670             ImmLogic::maybe_from_u64(0x7ffe, I64)
671         );
672 
673         assert_eq!(
674             Some(ImmLogic {
675                 value: 0x30000,
676                 n: true,
677                 r: 48,
678                 s: 1,
679                 size: InstSize::Size64,
680             }),
681             ImmLogic::maybe_from_u64(0x30000, I64)
682         );
683 
684         assert_eq!(
685             Some(ImmLogic {
686                 value: 0x100000,
687                 n: true,
688                 r: 44,
689                 s: 0,
690                 size: InstSize::Size64,
691             }),
692             ImmLogic::maybe_from_u64(0x100000, I64)
693         );
694 
695         assert_eq!(
696             Some(ImmLogic {
697                 value: u64::max_value() - 1,
698                 n: true,
699                 r: 63,
700                 s: 62,
701                 size: InstSize::Size64,
702             }),
703             ImmLogic::maybe_from_u64(u64::max_value() - 1, I64)
704         );
705 
706         assert_eq!(
707             Some(ImmLogic {
708                 value: 0xaaaaaaaaaaaaaaaa,
709                 n: false,
710                 r: 1,
711                 s: 60,
712                 size: InstSize::Size64,
713             }),
714             ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64)
715         );
716 
717         assert_eq!(
718             Some(ImmLogic {
719                 value: 0x8181818181818181,
720                 n: false,
721                 r: 1,
722                 s: 49,
723                 size: InstSize::Size64,
724             }),
725             ImmLogic::maybe_from_u64(0x8181818181818181, I64)
726         );
727 
728         assert_eq!(
729             Some(ImmLogic {
730                 value: 0xffc3ffc3ffc3ffc3,
731                 n: false,
732                 r: 10,
733                 s: 43,
734                 size: InstSize::Size64,
735             }),
736             ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64)
737         );
738 
739         assert_eq!(
740             Some(ImmLogic {
741                 value: 0x100000001,
742                 n: false,
743                 r: 0,
744                 s: 0,
745                 size: InstSize::Size64,
746             }),
747             ImmLogic::maybe_from_u64(0x100000001, I64)
748         );
749 
750         assert_eq!(
751             Some(ImmLogic {
752                 value: 0x1111111111111111,
753                 n: false,
754                 r: 0,
755                 s: 56,
756                 size: InstSize::Size64,
757             }),
758             ImmLogic::maybe_from_u64(0x1111111111111111, I64)
759         );
760 
761         for n in 0..2 {
762             let types = if n == 0 { vec![I64, I32] } else { vec![I64] };
763             for s in 0..64 {
764                 for r in 0..64 {
765                     let imm = get_logical_imm(n, s, r);
766                     for &ty in &types {
767                         match ImmLogic::maybe_from_u64(imm, ty) {
768                             Some(ImmLogic { value, .. }) => {
769                                 assert_eq!(imm, value);
770                                 ImmLogic::maybe_from_u64(!value, ty).unwrap();
771                             }
772                             None => assert_eq!(0, imm),
773                         };
774                     }
775                 }
776             }
777         }
778     }
779 
780     // Repeat a value that has `width` bits, across a 64-bit value.
repeat(value: u64, width: u64) -> u64781     fn repeat(value: u64, width: u64) -> u64 {
782         let mut result = value & ((1 << width) - 1);
783         let mut i = width;
784         while i < 64 {
785             result |= result << i;
786             i *= 2;
787         }
788         result
789     }
790 
791     // Get the logical immediate, from the encoding N/R/S bits.
get_logical_imm(n: u32, s: u32, r: u32) -> u64792     fn get_logical_imm(n: u32, s: u32, r: u32) -> u64 {
793         // An integer is constructed from the n, imm_s and imm_r bits according to
794         // the following table:
795         //
796         //  N   imms    immr    size        S             R
797         //  1  ssssss  rrrrrr    64    UInt(ssssss)  UInt(rrrrrr)
798         //  0  0sssss  xrrrrr    32    UInt(sssss)   UInt(rrrrr)
799         //  0  10ssss  xxrrrr    16    UInt(ssss)    UInt(rrrr)
800         //  0  110sss  xxxrrr     8    UInt(sss)     UInt(rrr)
801         //  0  1110ss  xxxxrr     4    UInt(ss)      UInt(rr)
802         //  0  11110s  xxxxxr     2    UInt(s)       UInt(r)
803         // (s bits must not be all set)
804         //
805         // A pattern is constructed of size bits, where the least significant S+1
806         // bits are set. The pattern is rotated right by R, and repeated across a
807         // 64-bit value.
808 
809         if n == 1 {
810             if s == 0x3f {
811                 return 0;
812             }
813             let bits = (1u64 << (s + 1)) - 1;
814             bits.rotate_right(r)
815         } else {
816             if (s >> 1) == 0x1f {
817                 return 0;
818             }
819             let mut width = 0x20;
820             while width >= 0x2 {
821                 if (s & width) == 0 {
822                     let mask = width - 1;
823                     if (s & mask) == mask {
824                         return 0;
825                     }
826                     let bits = (1u64 << ((s & mask) + 1)) - 1;
827                     return repeat(bits.rotate_right(r & mask), width.into());
828                 }
829                 width >>= 1;
830             }
831             unreachable!();
832         }
833     }
834 }
835