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