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