1 //! AArch64 ISA definitions: immediate constants. 2 3 use crate::ir::types::*; 4 use crate::isa::aarch64::inst::{OperandSize, ScalarSize}; 5 use crate::machinst::PrettyPrint; 6 7 use alloc::string::String; 8 9 /// An immediate that represents the NZCV flags. 10 #[derive(Clone, Copy, Debug)] 11 pub 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 22 impl NZCV { 23 /// Create a new NZCV flags representation. new(n: bool, z: bool, c: bool, v: bool) -> NZCV24 pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV { 25 NZCV { n, z, c, v } 26 } 27 28 /// Bits for encoding. bits(&self) -> u3229 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)] 39 pub struct UImm5 { 40 /// The value. 41 value: u8, 42 } 43 44 impl UImm5 { 45 /// Create an unsigned 5-bit immediate from u8. maybe_from_u8(value: u8) -> Option<UImm5>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. bits(&self) -> u3255 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)] 62 pub struct SImm7Scaled { 63 /// The value. 64 pub value: i16, 65 /// multiplied by the size of this type 66 pub scale_ty: Type, 67 } 68 69 impl SImm7Scaled { 70 /// Create a SImm7Scaled from a raw offset and the known scale type, if 71 /// possible. maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled>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. bits(&self) -> u3290 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)] 102 pub struct FPULeftShiftImm { 103 /// Shift amount. 104 pub amount: u8, 105 /// Lane size in bits. 106 pub lane_size_in_bits: u8, 107 } 108 109 impl FPULeftShiftImm { 110 /// Create a floating-point unit immediate left shift from u8. maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self>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. enc(&self) -> u32124 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)] 147 pub struct FPURightShiftImm { 148 /// Shift amount. 149 pub amount: u8, 150 /// Lane size in bits. 151 pub lane_size_in_bits: u8, 152 } 153 154 impl FPURightShiftImm { 155 /// Create a floating-point unit immediate right shift from u8. maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self>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. enc(&self) -> u32169 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)] 191 pub struct SImm9 { 192 /// The value. 193 pub value: i16, 194 } 195 196 impl SImm9 { 197 /// Create a signed 9-bit offset from a full-range value, if possible. maybe_from_i64(value: i64) -> Option<SImm9>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. bits(&self) -> u32209 pub fn bits(&self) -> u32 { 210 (self.value as u32) & 0x1ff 211 } 212 213 /// Signed value of immediate. value(&self) -> i32214 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)] 221 pub struct UImm12Scaled { 222 /// The value. 223 value: u16, 224 /// multiplied by the size of this type 225 scale_ty: Type, 226 } 227 228 impl UImm12Scaled { 229 /// Create a UImm12Scaled from a raw offset and the known scale type, if 230 /// possible. maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled>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. zero(scale_ty: Type) -> UImm12Scaled247 pub fn zero(scale_ty: Type) -> UImm12Scaled { 248 UImm12Scaled { value: 0, scale_ty } 249 } 250 251 /// Encoded bits. bits(&self) -> u32252 pub fn bits(&self) -> u32 { 253 (self.value as u32 / self.scale_ty.bytes()) & 0xfff 254 } 255 256 /// Value after scaling. value(&self) -> u32257 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)] 265 pub 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 272 impl 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. maybe_from_u64(val: u64) -> Option<Imm12>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. shift_bits(&self) -> u32297 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. imm_bits(&self) -> u32302 pub fn imm_bits(&self) -> u32 { 303 self.bits as u32 304 } 305 306 /// Get the actual value that this immediate corresponds to. value(&self) -> u32307 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)] 315 pub 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 328 impl ImmLogic { 329 /// Compute an ImmLogic from raw bits, if possible. maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic>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) enc_bits(&self) -> u32522 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. value(&self) -> u64527 pub fn value(&self) -> u64 { 528 self.value 529 } 530 531 /// Return an immediate for the bitwise-inverted value. invert(&self) -> ImmLogic532 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)] 540 pub struct ImmShift { 541 /// 6-bit shift amount. 542 pub imm: u8, 543 } 544 545 impl ImmShift { 546 /// Create an ImmShift from raw bits, if possible. maybe_from_u64(val: u64) -> Option<ImmShift>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. value(&self) -> u8556 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)] 563 pub 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 570 impl MoveWideConst { 571 /// Construct a MoveWideConst from an arbitrary 64-bit constant if possible. maybe_from_u64(value: u64) -> Option<MoveWideConst>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 `MoveWideConst` from a given shift, if possible. maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst>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. zero() -> MoveWideConst619 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)] 626 pub struct ASIMDMovModImm { 627 imm: u8, 628 shift: u8, 629 is_64bit: bool, 630 shift_ones: bool, 631 } 632 633 impl 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. maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm>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. zero(size: ScalarSize) -> Self732 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. value(&self) -> (u8, u32, bool)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)] 749 pub struct ASIMDFPModImm { 750 imm: u8, 751 size: ScalarSize, 752 } 753 754 impl ASIMDFPModImm { 755 /// Construct an ASIMDFPModImm from an arbitrary 64-bit constant, if possible. maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDFPModImm>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. enc_bits(&self) -> u8809 pub fn enc_bits(&self) -> u8 { 810 self.imm 811 } 812 813 /// Returns the 16-bit value that corresponds to an 8-bit encoding. value16(imm: u8) -> u16814 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. value32(imm: u8) -> u32825 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. value64(imm: u8) -> u64836 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 847 impl PrettyPrint for NZCV { pretty_print(&self, _: u8) -> String848 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 860 impl PrettyPrint for UImm5 { pretty_print(&self, _: u8) -> String861 fn pretty_print(&self, _: u8) -> String { 862 format!("#{}", self.value) 863 } 864 } 865 866 impl PrettyPrint for Imm12 { pretty_print(&self, _: u8) -> String867 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 874 impl PrettyPrint for SImm7Scaled { pretty_print(&self, _: u8) -> String875 fn pretty_print(&self, _: u8) -> String { 876 format!("#{}", self.value) 877 } 878 } 879 880 impl PrettyPrint for FPULeftShiftImm { pretty_print(&self, _: u8) -> String881 fn pretty_print(&self, _: u8) -> String { 882 format!("#{}", self.amount) 883 } 884 } 885 886 impl PrettyPrint for FPURightShiftImm { pretty_print(&self, _: u8) -> String887 fn pretty_print(&self, _: u8) -> String { 888 format!("#{}", self.amount) 889 } 890 } 891 892 impl PrettyPrint for SImm9 { pretty_print(&self, _: u8) -> String893 fn pretty_print(&self, _: u8) -> String { 894 format!("#{}", self.value) 895 } 896 } 897 898 impl PrettyPrint for UImm12Scaled { pretty_print(&self, _: u8) -> String899 fn pretty_print(&self, _: u8) -> String { 900 format!("#{}", self.value) 901 } 902 } 903 904 impl PrettyPrint for ImmLogic { pretty_print(&self, _: u8) -> String905 fn pretty_print(&self, _: u8) -> String { 906 format!("#{}", self.value()) 907 } 908 } 909 910 impl PrettyPrint for ImmShift { pretty_print(&self, _: u8) -> String911 fn pretty_print(&self, _: u8) -> String { 912 format!("#{}", self.imm) 913 } 914 } 915 916 impl PrettyPrint for MoveWideConst { pretty_print(&self, _: u8) -> String917 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 926 impl PrettyPrint for ASIMDMovModImm { pretty_print(&self, _: u8) -> String927 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 950 impl PrettyPrint for ASIMDFPModImm { pretty_print(&self, _: u8) -> String951 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)] 972 mod test { 973 use super::*; 974 975 #[test] imm_logical_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. repeat(value: u64, width: u64) -> u641149 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. get_logical_imm(n: u32, s: u32, r: u32) -> u641160 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] asimd_fp_mod_imm_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] asimd_mov_mod_imm_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 } 1243