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