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