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::InstSize; 8 use crate::machinst::*; 9 10 use regalloc::RealRegUniverse; 11 12 use core::convert::TryFrom; 13 use std::string::String; 14 15 /// An immediate that represents the NZCV flags. 16 #[derive(Clone, Copy, Debug)] 17 pub struct NZCV { 18 /// The negative condition flag. 19 n: bool, 20 /// The zero condition flag. 21 z: bool, 22 /// The carry condition flag. 23 c: bool, 24 /// The overflow condition flag. 25 v: bool, 26 } 27 28 impl NZCV { new(n: bool, z: bool, c: bool, v: bool) -> NZCV29 pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV { 30 NZCV { n, z, c, v } 31 } 32 33 /// Bits for encoding. bits(&self) -> u3234 pub fn bits(&self) -> u32 { 35 (u32::from(self.n) << 3) 36 | (u32::from(self.z) << 2) 37 | (u32::from(self.c) << 1) 38 | u32::from(self.v) 39 } 40 } 41 42 /// An unsigned 5-bit immediate. 43 #[derive(Clone, Copy, Debug)] 44 pub struct UImm5 { 45 /// The value. 46 value: u8, 47 } 48 49 impl UImm5 { maybe_from_u8(value: u8) -> Option<UImm5>50 pub fn maybe_from_u8(value: u8) -> Option<UImm5> { 51 if value < 32 { 52 Some(UImm5 { value }) 53 } else { 54 None 55 } 56 } 57 58 /// Bits for encoding. bits(&self) -> u3259 pub fn bits(&self) -> u32 { 60 u32::from(self.value) 61 } 62 } 63 64 /// A signed, scaled 7-bit offset. 65 #[derive(Clone, Copy, Debug)] 66 pub struct SImm7Scaled { 67 /// The value. 68 pub value: i16, 69 /// multiplied by the size of this type 70 pub scale_ty: Type, 71 } 72 73 impl SImm7Scaled { 74 /// Create a SImm7Scaled from a raw offset and the known scale type, if 75 /// possible. maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled>76 pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled> { 77 assert!(scale_ty == I64 || scale_ty == I32); 78 let scale = scale_ty.bytes(); 79 assert!(scale.is_power_of_two()); 80 let scale = i64::from(scale); 81 let upper_limit = 63 * scale; 82 let lower_limit = -(64 * scale); 83 if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 { 84 Some(SImm7Scaled { 85 value: i16::try_from(value).unwrap(), 86 scale_ty, 87 }) 88 } else { 89 None 90 } 91 } 92 93 /// Create a zero immediate of this format. zero(scale_ty: Type) -> SImm7Scaled94 pub fn zero(scale_ty: Type) -> SImm7Scaled { 95 SImm7Scaled { value: 0, scale_ty } 96 } 97 98 /// Bits for encoding. bits(&self) -> u3299 pub fn bits(&self) -> u32 { 100 let ty_bytes: i16 = self.scale_ty.bytes() as i16; 101 let scaled: i16 = self.value / ty_bytes; 102 assert!(scaled <= 63 && scaled >= -64); 103 let scaled: i8 = scaled as i8; 104 let encoded: u32 = scaled as u32; 105 encoded & 0x7f 106 } 107 } 108 109 /// a 9-bit signed offset. 110 #[derive(Clone, Copy, Debug)] 111 pub struct SImm9 { 112 /// The value. 113 pub value: i16, 114 } 115 116 impl SImm9 { 117 /// Create a signed 9-bit offset from a full-range value, if possible. maybe_from_i64(value: i64) -> Option<SImm9>118 pub fn maybe_from_i64(value: i64) -> Option<SImm9> { 119 if value >= -256 && value <= 255 { 120 Some(SImm9 { 121 value: value as i16, 122 }) 123 } else { 124 None 125 } 126 } 127 128 /// Create a zero immediate of this format. zero() -> SImm9129 pub fn zero() -> SImm9 { 130 SImm9 { value: 0 } 131 } 132 133 /// Bits for encoding. bits(&self) -> u32134 pub fn bits(&self) -> u32 { 135 (self.value as u32) & 0x1ff 136 } 137 } 138 139 /// An unsigned, scaled 12-bit offset. 140 #[derive(Clone, Copy, Debug)] 141 pub struct UImm12Scaled { 142 /// The value. 143 pub value: u16, 144 /// multiplied by the size of this type 145 pub scale_ty: Type, 146 } 147 148 impl UImm12Scaled { 149 /// Create a UImm12Scaled from a raw offset and the known scale type, if 150 /// possible. maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled>151 pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled> { 152 let scale = scale_ty.bytes(); 153 assert!(scale.is_power_of_two()); 154 let scale = scale as i64; 155 let limit = 4095 * scale; 156 if value >= 0 && value <= limit && (value & (scale - 1)) == 0 { 157 Some(UImm12Scaled { 158 value: value as u16, 159 scale_ty, 160 }) 161 } else { 162 None 163 } 164 } 165 166 /// Create a zero immediate of this format. zero(scale_ty: Type) -> UImm12Scaled167 pub fn zero(scale_ty: Type) -> UImm12Scaled { 168 UImm12Scaled { value: 0, scale_ty } 169 } 170 171 /// Encoded bits. bits(&self) -> u32172 pub fn bits(&self) -> u32 { 173 (self.value as u32 / self.scale_ty.bytes()) & 0xfff 174 } 175 } 176 177 /// A shifted immediate value in 'imm12' format: supports 12 bits, shifted 178 /// left by 0 or 12 places. 179 #[derive(Clone, Debug)] 180 pub struct Imm12 { 181 /// The immediate bits. 182 pub bits: u16, 183 /// Whether the immediate bits are shifted left by 12 or not. 184 pub shift12: bool, 185 } 186 187 impl Imm12 { 188 /// Compute a Imm12 from raw bits, if possible. maybe_from_u64(val: u64) -> Option<Imm12>189 pub fn maybe_from_u64(val: u64) -> Option<Imm12> { 190 if val == 0 { 191 Some(Imm12 { 192 bits: 0, 193 shift12: false, 194 }) 195 } else if val < 0xfff { 196 Some(Imm12 { 197 bits: val as u16, 198 shift12: false, 199 }) 200 } else if val < 0xfff_000 && (val & 0xfff == 0) { 201 Some(Imm12 { 202 bits: (val >> 12) as u16, 203 shift12: true, 204 }) 205 } else { 206 None 207 } 208 } 209 210 /// Bits for 2-bit "shift" field in e.g. AddI. shift_bits(&self) -> u32211 pub fn shift_bits(&self) -> u32 { 212 if self.shift12 { 213 0b01 214 } else { 215 0b00 216 } 217 } 218 219 /// Bits for 12-bit "imm" field in e.g. AddI. imm_bits(&self) -> u32220 pub fn imm_bits(&self) -> u32 { 221 self.bits as u32 222 } 223 } 224 225 /// An immediate for logical instructions. 226 #[derive(Clone, Debug)] 227 #[cfg_attr(test, derive(PartialEq))] 228 pub struct ImmLogic { 229 /// The actual value. 230 value: u64, 231 /// `N` flag. 232 pub n: bool, 233 /// `S` field: element size and element bits. 234 pub r: u8, 235 /// `R` field: rotate amount. 236 pub s: u8, 237 /// Was this constructed for a 32-bit or 64-bit instruction? 238 pub size: InstSize, 239 } 240 241 impl ImmLogic { 242 /// Compute an ImmLogic from raw bits, if possible. maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic>243 pub fn maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic> { 244 // Note: This function is a port of VIXL's Assembler::IsImmLogical. 245 246 if ty != I64 && ty != I32 { 247 return None; 248 } 249 let inst_size = InstSize::from_ty(ty); 250 251 let original_value = value; 252 253 let value = if ty == I32 { 254 // To handle 32-bit logical immediates, the very easiest thing is to repeat 255 // the input value twice to make a 64-bit word. The correct encoding of that 256 // as a logical immediate will also be the correct encoding of the 32-bit 257 // value. 258 259 // Avoid making the assumption that the most-significant 32 bits are zero by 260 // shifting the value left and duplicating it. 261 let value = value << 32; 262 value | value >> 32 263 } else { 264 value 265 }; 266 267 // Logical immediates are encoded using parameters n, imm_s and imm_r using 268 // the following table: 269 // 270 // N imms immr size S R 271 // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) 272 // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) 273 // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) 274 // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) 275 // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) 276 // 0 11110s xxxxxr 2 UInt(s) UInt(r) 277 // (s bits must not be all set) 278 // 279 // A pattern is constructed of size bits, where the least significant S+1 bits 280 // are set. The pattern is rotated right by R, and repeated across a 32 or 281 // 64-bit value, depending on destination register width. 282 // 283 // Put another way: the basic format of a logical immediate is a single 284 // contiguous stretch of 1 bits, repeated across the whole word at intervals 285 // given by a power of 2. To identify them quickly, we first locate the 286 // lowest stretch of 1 bits, then the next 1 bit above that; that combination 287 // is different for every logical immediate, so it gives us all the 288 // information we need to identify the only logical immediate that our input 289 // could be, and then we simply check if that's the value we actually have. 290 // 291 // (The rotation parameter does give the possibility of the stretch of 1 bits 292 // going 'round the end' of the word. To deal with that, we observe that in 293 // any situation where that happens the bitwise NOT of the value is also a 294 // valid logical immediate. So we simply invert the input whenever its low bit 295 // is set, and then we know that the rotated case can't arise.) 296 let (value, inverted) = if value & 1 == 1 { 297 (!value, true) 298 } else { 299 (value, false) 300 }; 301 302 if value == 0 { 303 return None; 304 } 305 306 // The basic analysis idea: imagine our input word looks like this. 307 // 308 // 0011111000111110001111100011111000111110001111100011111000111110 309 // c b a 310 // |<--d-->| 311 // 312 // We find the lowest set bit (as an actual power-of-2 value, not its index) 313 // and call it a. Then we add a to our original number, which wipes out the 314 // bottommost stretch of set bits and replaces it with a 1 carried into the 315 // next zero bit. Then we look for the new lowest set bit, which is in 316 // position b, and subtract it, so now our number is just like the original 317 // but with the lowest stretch of set bits completely gone. Now we find the 318 // lowest set bit again, which is position c in the diagram above. Then we'll 319 // measure the distance d between bit positions a and c (using CLZ), and that 320 // tells us that the only valid logical immediate that could possibly be equal 321 // to this number is the one in which a stretch of bits running from a to just 322 // below b is replicated every d bits. 323 fn lowest_set_bit(value: u64) -> u64 { 324 let bit = value.trailing_zeros(); 325 1u64.checked_shl(bit).unwrap_or(0) 326 } 327 let a = lowest_set_bit(value); 328 assert_ne!(0, a); 329 let value_plus_a = value.wrapping_add(a); 330 let b = lowest_set_bit(value_plus_a); 331 let value_plus_a_minus_b = value_plus_a - b; 332 let c = lowest_set_bit(value_plus_a_minus_b); 333 334 let (d, clz_a, out_n, mask) = if c != 0 { 335 // The general case, in which there is more than one stretch of set bits. 336 // Compute the repeat distance d, and set up a bitmask covering the basic 337 // unit of repetition (i.e. a word with the bottom d bits set). Also, in all 338 // of these cases the N bit of the output will be zero. 339 let clz_a = a.leading_zeros(); 340 let clz_c = c.leading_zeros(); 341 let d = clz_a - clz_c; 342 let mask = (1 << d) - 1; 343 (d, clz_a, 0, mask) 344 } else { 345 (64, a.leading_zeros(), 1, u64::max_value()) 346 }; 347 348 // If the repeat period d is not a power of two, it can't be encoded. 349 if !d.is_power_of_two() { 350 return None; 351 } 352 353 if ((b.wrapping_sub(a)) & !mask) != 0 { 354 // If the bit stretch (b - a) does not fit within the mask derived from the 355 // repeat period, then fail. 356 return None; 357 } 358 359 // The only possible option is b - a repeated every d bits. Now we're going to 360 // actually construct the valid logical immediate derived from that 361 // specification, and see if it equals our original input. 362 // 363 // To repeat a value every d bits, we multiply it by a number of the form 364 // (1 + 2^d + 2^(2d) + ...), i.e. 0x0001000100010001 or similar. These can 365 // be derived using a table lookup on CLZ(d). 366 const MULTIPLIERS: [u64; 6] = [ 367 0x0000000000000001, 368 0x0000000100000001, 369 0x0001000100010001, 370 0x0101010101010101, 371 0x1111111111111111, 372 0x5555555555555555, 373 ]; 374 let multiplier = MULTIPLIERS[(u64::from(d).leading_zeros() - 57) as usize]; 375 let candidate = b.wrapping_sub(a) * multiplier; 376 377 if value != candidate { 378 // The candidate pattern doesn't match our input value, so fail. 379 return None; 380 } 381 382 // We have a match! This is a valid logical immediate, so now we have to 383 // construct the bits and pieces of the instruction encoding that generates 384 // it. 385 386 // Count the set bits in our basic stretch. The special case of clz(0) == -1 387 // makes the answer come out right for stretches that reach the very top of 388 // the word (e.g. numbers like 0xffffc00000000000). 389 let clz_b = if b == 0 { 390 u32::max_value() // -1 391 } else { 392 b.leading_zeros() 393 }; 394 let s = clz_a.wrapping_sub(clz_b); 395 396 // Decide how many bits to rotate right by, to put the low bit of that basic 397 // stretch in position a. 398 let (s, r) = if inverted { 399 // If we inverted the input right at the start of this function, here's 400 // where we compensate: the number of set bits becomes the number of clear 401 // bits, and the rotation count is based on position b rather than position 402 // a (since b is the location of the 'lowest' 1 bit after inversion). 403 // Need wrapping for when clz_b is max_value() (for when b == 0). 404 (d - s, clz_b.wrapping_add(1) & (d - 1)) 405 } else { 406 (s, (clz_a + 1) & (d - 1)) 407 }; 408 409 // Now we're done, except for having to encode the S output in such a way that 410 // it gives both the number of set bits and the length of the repeated 411 // segment. The s field is encoded like this: 412 // 413 // imms size S 414 // ssssss 64 UInt(ssssss) 415 // 0sssss 32 UInt(sssss) 416 // 10ssss 16 UInt(ssss) 417 // 110sss 8 UInt(sss) 418 // 1110ss 4 UInt(ss) 419 // 11110s 2 UInt(s) 420 // 421 // So we 'or' (2 * -d) with our computed s to form imms. 422 let s = ((d * 2).wrapping_neg() | (s - 1)) & 0x3f; 423 debug_assert!(u8::try_from(r).is_ok()); 424 debug_assert!(u8::try_from(s).is_ok()); 425 Some(ImmLogic { 426 value: original_value, 427 n: out_n != 0, 428 r: r as u8, 429 s: s as u8, 430 size: inst_size, 431 }) 432 } 433 434 /// Returns bits ready for encoding: (N:1, R:6, S:6) enc_bits(&self) -> u32435 pub fn enc_bits(&self) -> u32 { 436 ((self.n as u32) << 12) | ((self.r as u32) << 6) | (self.s as u32) 437 } 438 439 /// Returns the value that this immediate represents. value(&self) -> u64440 pub fn value(&self) -> u64 { 441 self.value 442 } 443 444 /// Return an immediate for the bitwise-inverted value. invert(&self) -> ImmLogic445 pub fn invert(&self) -> ImmLogic { 446 // For every ImmLogical immediate, the inverse can also be encoded. 447 Self::maybe_from_u64(!self.value, self.size.to_ty()).unwrap() 448 } 449 } 450 451 /// An immediate for shift instructions. 452 #[derive(Clone, Debug)] 453 pub struct ImmShift { 454 /// 6-bit shift amount. 455 pub imm: u8, 456 } 457 458 impl ImmShift { 459 /// Create an ImmShift from raw bits, if possible. maybe_from_u64(val: u64) -> Option<ImmShift>460 pub fn maybe_from_u64(val: u64) -> Option<ImmShift> { 461 if val < 64 { 462 Some(ImmShift { imm: val as u8 }) 463 } else { 464 None 465 } 466 } 467 468 /// Get the immediate value. value(&self) -> u8469 pub fn value(&self) -> u8 { 470 self.imm 471 } 472 } 473 474 /// A 16-bit immediate for a MOVZ instruction, with a {0,16,32,48}-bit shift. 475 #[derive(Clone, Copy, Debug)] 476 pub struct MoveWideConst { 477 /// The value. 478 pub bits: u16, 479 /// Result is `bits` shifted 16*shift bits to the left. 480 pub shift: u8, 481 } 482 483 impl MoveWideConst { 484 /// Construct a MoveWideConst from an arbitrary 64-bit constant if possible. maybe_from_u64(value: u64) -> Option<MoveWideConst>485 pub fn maybe_from_u64(value: u64) -> Option<MoveWideConst> { 486 let mask0 = 0x0000_0000_0000_ffffu64; 487 let mask1 = 0x0000_0000_ffff_0000u64; 488 let mask2 = 0x0000_ffff_0000_0000u64; 489 let mask3 = 0xffff_0000_0000_0000u64; 490 491 if value == (value & mask0) { 492 return Some(MoveWideConst { 493 bits: (value & mask0) as u16, 494 shift: 0, 495 }); 496 } 497 if value == (value & mask1) { 498 return Some(MoveWideConst { 499 bits: ((value >> 16) & mask0) as u16, 500 shift: 1, 501 }); 502 } 503 if value == (value & mask2) { 504 return Some(MoveWideConst { 505 bits: ((value >> 32) & mask0) as u16, 506 shift: 2, 507 }); 508 } 509 if value == (value & mask3) { 510 return Some(MoveWideConst { 511 bits: ((value >> 48) & mask0) as u16, 512 shift: 3, 513 }); 514 } 515 None 516 } 517 maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst>518 pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> { 519 let shift_enc = shift / 16; 520 if shift_enc > 3 { 521 None 522 } else { 523 Some(MoveWideConst { 524 bits: imm, 525 shift: shift_enc, 526 }) 527 } 528 } 529 530 /// Returns the value that this constant represents. value(&self) -> u64531 pub fn value(&self) -> u64 { 532 (self.bits as u64) << (16 * self.shift) 533 } 534 } 535 536 impl ShowWithRRU for NZCV { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String537 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 538 let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c }; 539 format!( 540 "#{}{}{}{}", 541 fmt('n', self.n), 542 fmt('z', self.z), 543 fmt('c', self.c), 544 fmt('v', self.v) 545 ) 546 } 547 } 548 549 impl ShowWithRRU for UImm5 { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String550 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 551 format!("#{}", self.value) 552 } 553 } 554 555 impl ShowWithRRU for Imm12 { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String556 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 557 let shift = if self.shift12 { 12 } else { 0 }; 558 let value = u32::from(self.bits) << shift; 559 format!("#{}", value) 560 } 561 } 562 563 impl ShowWithRRU for SImm7Scaled { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String564 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 565 format!("#{}", self.value) 566 } 567 } 568 569 impl ShowWithRRU for SImm9 { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String570 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 571 format!("#{}", self.value) 572 } 573 } 574 575 impl ShowWithRRU for UImm12Scaled { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String576 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 577 format!("#{}", self.value) 578 } 579 } 580 581 impl ShowWithRRU for ImmLogic { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String582 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 583 format!("#{}", self.value()) 584 } 585 } 586 587 impl ShowWithRRU for ImmShift { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String588 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 589 format!("#{}", self.imm) 590 } 591 } 592 593 impl ShowWithRRU for MoveWideConst { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String594 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 595 if self.shift == 0 { 596 format!("#{}", self.bits) 597 } else { 598 format!("#{}, LSL #{}", self.bits, self.shift * 16) 599 } 600 } 601 } 602 603 #[cfg(test)] 604 mod test { 605 use super::*; 606 607 #[test] imm_logical_test()608 fn imm_logical_test() { 609 assert_eq!(None, ImmLogic::maybe_from_u64(0, I64)); 610 assert_eq!(None, ImmLogic::maybe_from_u64(u64::max_value(), I64)); 611 612 assert_eq!( 613 Some(ImmLogic { 614 value: 1, 615 n: true, 616 r: 0, 617 s: 0, 618 size: InstSize::Size64, 619 }), 620 ImmLogic::maybe_from_u64(1, I64) 621 ); 622 623 assert_eq!( 624 Some(ImmLogic { 625 value: 2, 626 n: true, 627 r: 63, 628 s: 0, 629 size: InstSize::Size64, 630 }), 631 ImmLogic::maybe_from_u64(2, I64) 632 ); 633 634 assert_eq!(None, ImmLogic::maybe_from_u64(5, I64)); 635 636 assert_eq!(None, ImmLogic::maybe_from_u64(11, I64)); 637 638 assert_eq!( 639 Some(ImmLogic { 640 value: 248, 641 n: true, 642 r: 61, 643 s: 4, 644 size: InstSize::Size64, 645 }), 646 ImmLogic::maybe_from_u64(248, I64) 647 ); 648 649 assert_eq!(None, ImmLogic::maybe_from_u64(249, I64)); 650 651 assert_eq!( 652 Some(ImmLogic { 653 value: 1920, 654 n: true, 655 r: 57, 656 s: 3, 657 size: InstSize::Size64, 658 }), 659 ImmLogic::maybe_from_u64(1920, I64) 660 ); 661 662 assert_eq!( 663 Some(ImmLogic { 664 value: 0x7ffe, 665 n: true, 666 r: 63, 667 s: 13, 668 size: InstSize::Size64, 669 }), 670 ImmLogic::maybe_from_u64(0x7ffe, I64) 671 ); 672 673 assert_eq!( 674 Some(ImmLogic { 675 value: 0x30000, 676 n: true, 677 r: 48, 678 s: 1, 679 size: InstSize::Size64, 680 }), 681 ImmLogic::maybe_from_u64(0x30000, I64) 682 ); 683 684 assert_eq!( 685 Some(ImmLogic { 686 value: 0x100000, 687 n: true, 688 r: 44, 689 s: 0, 690 size: InstSize::Size64, 691 }), 692 ImmLogic::maybe_from_u64(0x100000, I64) 693 ); 694 695 assert_eq!( 696 Some(ImmLogic { 697 value: u64::max_value() - 1, 698 n: true, 699 r: 63, 700 s: 62, 701 size: InstSize::Size64, 702 }), 703 ImmLogic::maybe_from_u64(u64::max_value() - 1, I64) 704 ); 705 706 assert_eq!( 707 Some(ImmLogic { 708 value: 0xaaaaaaaaaaaaaaaa, 709 n: false, 710 r: 1, 711 s: 60, 712 size: InstSize::Size64, 713 }), 714 ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64) 715 ); 716 717 assert_eq!( 718 Some(ImmLogic { 719 value: 0x8181818181818181, 720 n: false, 721 r: 1, 722 s: 49, 723 size: InstSize::Size64, 724 }), 725 ImmLogic::maybe_from_u64(0x8181818181818181, I64) 726 ); 727 728 assert_eq!( 729 Some(ImmLogic { 730 value: 0xffc3ffc3ffc3ffc3, 731 n: false, 732 r: 10, 733 s: 43, 734 size: InstSize::Size64, 735 }), 736 ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64) 737 ); 738 739 assert_eq!( 740 Some(ImmLogic { 741 value: 0x100000001, 742 n: false, 743 r: 0, 744 s: 0, 745 size: InstSize::Size64, 746 }), 747 ImmLogic::maybe_from_u64(0x100000001, I64) 748 ); 749 750 assert_eq!( 751 Some(ImmLogic { 752 value: 0x1111111111111111, 753 n: false, 754 r: 0, 755 s: 56, 756 size: InstSize::Size64, 757 }), 758 ImmLogic::maybe_from_u64(0x1111111111111111, I64) 759 ); 760 761 for n in 0..2 { 762 let types = if n == 0 { vec![I64, I32] } else { vec![I64] }; 763 for s in 0..64 { 764 for r in 0..64 { 765 let imm = get_logical_imm(n, s, r); 766 for &ty in &types { 767 match ImmLogic::maybe_from_u64(imm, ty) { 768 Some(ImmLogic { value, .. }) => { 769 assert_eq!(imm, value); 770 ImmLogic::maybe_from_u64(!value, ty).unwrap(); 771 } 772 None => assert_eq!(0, imm), 773 }; 774 } 775 } 776 } 777 } 778 } 779 780 // Repeat a value that has `width` bits, across a 64-bit value. repeat(value: u64, width: u64) -> u64781 fn repeat(value: u64, width: u64) -> u64 { 782 let mut result = value & ((1 << width) - 1); 783 let mut i = width; 784 while i < 64 { 785 result |= result << i; 786 i *= 2; 787 } 788 result 789 } 790 791 // Get the logical immediate, from the encoding N/R/S bits. get_logical_imm(n: u32, s: u32, r: u32) -> u64792 fn get_logical_imm(n: u32, s: u32, r: u32) -> u64 { 793 // An integer is constructed from the n, imm_s and imm_r bits according to 794 // the following table: 795 // 796 // N imms immr size S R 797 // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) 798 // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) 799 // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) 800 // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) 801 // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) 802 // 0 11110s xxxxxr 2 UInt(s) UInt(r) 803 // (s bits must not be all set) 804 // 805 // A pattern is constructed of size bits, where the least significant S+1 806 // bits are set. The pattern is rotated right by R, and repeated across a 807 // 64-bit value. 808 809 if n == 1 { 810 if s == 0x3f { 811 return 0; 812 } 813 let bits = (1u64 << (s + 1)) - 1; 814 bits.rotate_right(r) 815 } else { 816 if (s >> 1) == 0x1f { 817 return 0; 818 } 819 let mut width = 0x20; 820 while width >= 0x2 { 821 if (s & width) == 0 { 822 let mask = width - 1; 823 if (s & mask) == mask { 824 return 0; 825 } 826 let bits = (1u64 << ((s & mask) + 1)) - 1; 827 return repeat(bits.rotate_right(r & mask), width.into()); 828 } 829 width >>= 1; 830 } 831 unreachable!(); 832 } 833 } 834 } 835