Path: blob/main/cranelift/codegen/src/isa/aarch64/inst/imms.rs
1693 views
//! AArch64 ISA definitions: immediate constants.12use crate::ir::types::*;3use crate::isa::aarch64::inst::{OperandSize, ScalarSize};4use crate::machinst::PrettyPrint;56use std::string::String;78/// An immediate that represents the NZCV flags.9#[derive(Clone, Copy, Debug)]10pub struct NZCV {11/// The negative condition flag.12n: bool,13/// The zero condition flag.14z: bool,15/// The carry condition flag.16c: bool,17/// The overflow condition flag.18v: bool,19}2021impl NZCV {22/// Create a new NZCV flags representation.23pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV {24NZCV { n, z, c, v }25}2627/// Bits for encoding.28pub fn bits(&self) -> u32 {29(u32::from(self.n) << 3)30| (u32::from(self.z) << 2)31| (u32::from(self.c) << 1)32| u32::from(self.v)33}34}3536/// An unsigned 5-bit immediate.37#[derive(Clone, Copy, Debug)]38pub struct UImm5 {39/// The value.40value: u8,41}4243impl UImm5 {44/// Create an unsigned 5-bit immediate from u8.45pub fn maybe_from_u8(value: u8) -> Option<UImm5> {46if value < 32 {47Some(UImm5 { value })48} else {49None50}51}5253/// Bits for encoding.54pub fn bits(&self) -> u32 {55u32::from(self.value)56}57}5859/// A signed, scaled 7-bit offset.60#[derive(Clone, Copy, Debug)]61pub struct SImm7Scaled {62/// The value.63pub value: i16,64/// multiplied by the size of this type65pub scale_ty: Type,66}6768impl SImm7Scaled {69/// Create a SImm7Scaled from a raw offset and the known scale type, if70/// possible.71pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled> {72assert!(scale_ty == I64 || scale_ty == I32 || scale_ty == F64 || scale_ty == I8X16);73let scale = scale_ty.bytes();74assert!(scale.is_power_of_two());75let scale = i64::from(scale);76let upper_limit = 63 * scale;77let lower_limit = -(64 * scale);78if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 {79Some(SImm7Scaled {80value: i16::try_from(value).unwrap(),81scale_ty,82})83} else {84None85}86}8788/// Bits for encoding.89pub fn bits(&self) -> u32 {90let ty_bytes: i16 = self.scale_ty.bytes() as i16;91let scaled: i16 = self.value / ty_bytes;92assert!(scaled <= 63 && scaled >= -64);93let scaled: i8 = scaled as i8;94let encoded: u32 = scaled as u32;95encoded & 0x7f96}97}9899/// Floating-point unit immediate left shift.100#[derive(Clone, Copy, Debug)]101pub struct FPULeftShiftImm {102/// Shift amount.103pub amount: u8,104/// Lane size in bits.105pub lane_size_in_bits: u8,106}107108impl FPULeftShiftImm {109/// Create a floating-point unit immediate left shift from u8.110pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {111debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);112if amount < lane_size_in_bits {113Some(Self {114amount,115lane_size_in_bits,116})117} else {118None119}120}121122/// Returns the encoding of the immediate.123pub fn enc(&self) -> u32 {124debug_assert!(self.lane_size_in_bits.is_power_of_two());125debug_assert!(self.lane_size_in_bits > self.amount);126// The encoding of the immediate follows the table below,127// where xs encode the shift amount.128//129// | lane_size_in_bits | encoding |130// +------------------------------+131// | 8 | 0001xxx |132// | 16 | 001xxxx |133// | 32 | 01xxxxx |134// | 64 | 1xxxxxx |135//136// The highest one bit is represented by `lane_size_in_bits`. Since137// `lane_size_in_bits` is a power of 2 and `amount` is less138// than `lane_size_in_bits`, they can be ORed139// together to produced the encoded value.140u32::from(self.lane_size_in_bits | self.amount)141}142}143144/// Floating-point unit immediate right shift.145#[derive(Clone, Copy, Debug)]146pub struct FPURightShiftImm {147/// Shift amount.148pub amount: u8,149/// Lane size in bits.150pub lane_size_in_bits: u8,151}152153impl FPURightShiftImm {154/// Create a floating-point unit immediate right shift from u8.155pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {156debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);157if amount > 0 && amount <= lane_size_in_bits {158Some(Self {159amount,160lane_size_in_bits,161})162} else {163None164}165}166167/// Returns encoding of the immediate.168pub fn enc(&self) -> u32 {169debug_assert_ne!(0, self.amount);170// The encoding of the immediate follows the table below,171// where xs encodes the negated shift amount.172//173// | lane_size_in_bits | encoding |174// +------------------------------+175// | 8 | 0001xxx |176// | 16 | 001xxxx |177// | 32 | 01xxxxx |178// | 64 | 1xxxxxx |179//180// The shift amount is negated such that a shift amount181// of 1 (in 64-bit) is encoded as 0b111111 and a shift182// amount of 64 is encoded as 0b000000,183// in the bottom 6 bits.184u32::from((self.lane_size_in_bits * 2) - self.amount)185}186}187188/// a 9-bit signed offset.189#[derive(Clone, Copy, Debug)]190pub struct SImm9 {191/// The value.192pub value: i16,193}194195impl SImm9 {196/// Create a signed 9-bit offset from a full-range value, if possible.197pub fn maybe_from_i64(value: i64) -> Option<SImm9> {198if value >= -256 && value <= 255 {199Some(SImm9 {200value: value as i16,201})202} else {203None204}205}206207/// Bits for encoding.208pub fn bits(&self) -> u32 {209(self.value as u32) & 0x1ff210}211212/// Signed value of immediate.213pub fn value(&self) -> i32 {214self.value as i32215}216}217218/// An unsigned, scaled 12-bit offset.219#[derive(Clone, Copy, Debug)]220pub struct UImm12Scaled {221/// The value.222value: u16,223/// multiplied by the size of this type224scale_ty: Type,225}226227impl UImm12Scaled {228/// Create a UImm12Scaled from a raw offset and the known scale type, if229/// possible.230pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled> {231let scale = scale_ty.bytes();232assert!(scale.is_power_of_two());233let scale = scale as i64;234let limit = 4095 * scale;235if value >= 0 && value <= limit && (value & (scale - 1)) == 0 {236Some(UImm12Scaled {237value: value as u16,238scale_ty,239})240} else {241None242}243}244245/// Create a zero immediate of this format.246pub fn zero(scale_ty: Type) -> UImm12Scaled {247UImm12Scaled { value: 0, scale_ty }248}249250/// Encoded bits.251pub fn bits(&self) -> u32 {252(self.value as u32 / self.scale_ty.bytes()) & 0xfff253}254255/// Value after scaling.256pub fn value(&self) -> u32 {257self.value as u32258}259}260261/// A shifted immediate value in 'imm12' format: supports 12 bits, shifted262/// left by 0 or 12 places.263#[derive(Copy, Clone, Debug)]264pub struct Imm12 {265/// The immediate bits.266pub bits: u16,267/// Whether the immediate bits are shifted left by 12 or not.268pub shift12: bool,269}270271impl Imm12 {272/// Handy 0-value constant.273pub const ZERO: Imm12 = Imm12 {274bits: 0,275shift12: false,276};277278/// Compute a Imm12 from raw bits, if possible.279pub fn maybe_from_u64(val: u64) -> Option<Imm12> {280if val & !0xfff == 0 {281Some(Imm12 {282bits: val as u16,283shift12: false,284})285} else if val & !(0xfff << 12) == 0 {286Some(Imm12 {287bits: (val >> 12) as u16,288shift12: true,289})290} else {291None292}293}294295/// Bits for 2-bit "shift" field in e.g. AddI.296pub fn shift_bits(&self) -> u32 {297if self.shift12 { 0b01 } else { 0b00 }298}299300/// Bits for 12-bit "imm" field in e.g. AddI.301pub fn imm_bits(&self) -> u32 {302self.bits as u32303}304305/// Get the actual value that this immediate corresponds to.306pub fn value(&self) -> u32 {307let base = self.bits as u32;308if self.shift12 { base << 12 } else { base }309}310}311312/// An immediate for logical instructions.313#[derive(Copy, Clone, Debug, PartialEq)]314pub struct ImmLogic {315/// The actual value.316value: u64,317/// `N` flag.318pub n: bool,319/// `S` field: element size and element bits.320pub r: u8,321/// `R` field: rotate amount.322pub s: u8,323/// Was this constructed for a 32-bit or 64-bit instruction?324pub size: OperandSize,325}326327impl ImmLogic {328/// Compute an ImmLogic from raw bits, if possible.329pub fn maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic> {330// Note: This function is a port of VIXL's Assembler::IsImmLogical.331332if ty != I64 && ty != I32 {333return None;334}335let operand_size = OperandSize::from_ty(ty);336337let original_value = value;338339let value = if ty == I32 {340// To handle 32-bit logical immediates, the very easiest thing is to repeat341// the input value twice to make a 64-bit word. The correct encoding of that342// as a logical immediate will also be the correct encoding of the 32-bit343// value.344345// Avoid making the assumption that the most-significant 32 bits are zero by346// shifting the value left and duplicating it.347let value = value << 32;348value | value >> 32349} else {350value351};352353// Logical immediates are encoded using parameters n, imm_s and imm_r using354// the following table:355//356// N imms immr size S R357// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)358// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)359// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)360// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)361// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)362// 0 11110s xxxxxr 2 UInt(s) UInt(r)363// (s bits must not be all set)364//365// A pattern is constructed of size bits, where the least significant S+1 bits366// are set. The pattern is rotated right by R, and repeated across a 32 or367// 64-bit value, depending on destination register width.368//369// Put another way: the basic format of a logical immediate is a single370// contiguous stretch of 1 bits, repeated across the whole word at intervals371// given by a power of 2. To identify them quickly, we first locate the372// lowest stretch of 1 bits, then the next 1 bit above that; that combination373// is different for every logical immediate, so it gives us all the374// information we need to identify the only logical immediate that our input375// could be, and then we simply check if that's the value we actually have.376//377// (The rotation parameter does give the possibility of the stretch of 1 bits378// going 'round the end' of the word. To deal with that, we observe that in379// any situation where that happens the bitwise NOT of the value is also a380// valid logical immediate. So we simply invert the input whenever its low bit381// is set, and then we know that the rotated case can't arise.)382let (value, inverted) = if value & 1 == 1 {383(!value, true)384} else {385(value, false)386};387388if value == 0 {389return None;390}391392// The basic analysis idea: imagine our input word looks like this.393//394// 0011111000111110001111100011111000111110001111100011111000111110395// c b a396// |<--d-->|397//398// We find the lowest set bit (as an actual power-of-2 value, not its index)399// and call it a. Then we add a to our original number, which wipes out the400// bottommost stretch of set bits and replaces it with a 1 carried into the401// next zero bit. Then we look for the new lowest set bit, which is in402// position b, and subtract it, so now our number is just like the original403// but with the lowest stretch of set bits completely gone. Now we find the404// lowest set bit again, which is position c in the diagram above. Then we'll405// measure the distance d between bit positions a and c (using CLZ), and that406// tells us that the only valid logical immediate that could possibly be equal407// to this number is the one in which a stretch of bits running from a to just408// below b is replicated every d bits.409fn lowest_set_bit(value: u64) -> u64 {410let bit = value.trailing_zeros();4111u64.checked_shl(bit).unwrap_or(0)412}413let a = lowest_set_bit(value);414assert_ne!(0, a);415let value_plus_a = value.wrapping_add(a);416let b = lowest_set_bit(value_plus_a);417let value_plus_a_minus_b = value_plus_a - b;418let c = lowest_set_bit(value_plus_a_minus_b);419420let (d, clz_a, out_n, mask) = if c != 0 {421// The general case, in which there is more than one stretch of set bits.422// Compute the repeat distance d, and set up a bitmask covering the basic423// unit of repetition (i.e. a word with the bottom d bits set). Also, in all424// of these cases the N bit of the output will be zero.425let clz_a = a.leading_zeros();426let clz_c = c.leading_zeros();427let d = clz_a - clz_c;428let mask = (1 << d) - 1;429(d, clz_a, 0, mask)430} else {431(64, a.leading_zeros(), 1, u64::max_value())432};433434// If the repeat period d is not a power of two, it can't be encoded.435if !d.is_power_of_two() {436return None;437}438439if ((b.wrapping_sub(a)) & !mask) != 0 {440// If the bit stretch (b - a) does not fit within the mask derived from the441// repeat period, then fail.442return None;443}444445// The only possible option is b - a repeated every d bits. Now we're going to446// actually construct the valid logical immediate derived from that447// specification, and see if it equals our original input.448//449// To repeat a value every d bits, we multiply it by a number of the form450// (1 + 2^d + 2^(2d) + ...), i.e. 0x0001000100010001 or similar. These can451// be derived using a table lookup on CLZ(d).452const MULTIPLIERS: [u64; 6] = [4530x0000000000000001,4540x0000000100000001,4550x0001000100010001,4560x0101010101010101,4570x1111111111111111,4580x5555555555555555,459];460let multiplier = MULTIPLIERS[(u64::from(d).leading_zeros() - 57) as usize];461let candidate = b.wrapping_sub(a) * multiplier;462463if value != candidate {464// The candidate pattern doesn't match our input value, so fail.465return None;466}467468// We have a match! This is a valid logical immediate, so now we have to469// construct the bits and pieces of the instruction encoding that generates470// it.471472// Count the set bits in our basic stretch. The special case of clz(0) == -1473// makes the answer come out right for stretches that reach the very top of474// the word (e.g. numbers like 0xffffc00000000000).475let clz_b = if b == 0 {476u32::max_value() // -1477} else {478b.leading_zeros()479};480let s = clz_a.wrapping_sub(clz_b);481482// Decide how many bits to rotate right by, to put the low bit of that basic483// stretch in position a.484let (s, r) = if inverted {485// If we inverted the input right at the start of this function, here's486// where we compensate: the number of set bits becomes the number of clear487// bits, and the rotation count is based on position b rather than position488// a (since b is the location of the 'lowest' 1 bit after inversion).489// Need wrapping for when clz_b is max_value() (for when b == 0).490(d - s, clz_b.wrapping_add(1) & (d - 1))491} else {492(s, (clz_a + 1) & (d - 1))493};494495// Now we're done, except for having to encode the S output in such a way that496// it gives both the number of set bits and the length of the repeated497// segment. The s field is encoded like this:498//499// imms size S500// ssssss 64 UInt(ssssss)501// 0sssss 32 UInt(sssss)502// 10ssss 16 UInt(ssss)503// 110sss 8 UInt(sss)504// 1110ss 4 UInt(ss)505// 11110s 2 UInt(s)506//507// So we 'or' (2 * -d) with our computed s to form imms.508let s = ((d * 2).wrapping_neg() | (s - 1)) & 0x3f;509debug_assert!(u8::try_from(r).is_ok());510debug_assert!(u8::try_from(s).is_ok());511Some(ImmLogic {512value: original_value,513n: out_n != 0,514r: r as u8,515s: s as u8,516size: operand_size,517})518}519520/// Returns bits ready for encoding: (N:1, R:6, S:6)521pub fn enc_bits(&self) -> u32 {522((self.n as u32) << 12) | ((self.r as u32) << 6) | (self.s as u32)523}524525/// Returns the value that this immediate represents.526pub fn value(&self) -> u64 {527self.value528}529530/// Return an immediate for the bitwise-inverted value.531pub fn invert(&self) -> ImmLogic {532// For every ImmLogical immediate, the inverse can also be encoded.533Self::maybe_from_u64(!self.value, self.size.to_ty()).unwrap()534}535}536537/// An immediate for shift instructions.538#[derive(Copy, Clone, Debug)]539pub struct ImmShift {540/// 6-bit shift amount.541pub imm: u8,542}543544impl ImmShift {545/// Create an ImmShift from raw bits, if possible.546pub fn maybe_from_u64(val: u64) -> Option<ImmShift> {547if val < 64 {548Some(ImmShift { imm: val as u8 })549} else {550None551}552}553554/// Get the immediate value.555pub fn value(&self) -> u8 {556self.imm557}558}559560/// A 16-bit immediate for a MOVZ instruction, with a {0,16,32,48}-bit shift.561#[derive(Clone, Copy, Debug)]562pub struct MoveWideConst {563/// The value.564pub bits: u16,565/// Result is `bits` shifted 16*shift bits to the left.566pub shift: u8,567}568569impl MoveWideConst {570/// Construct a MoveWideConst from an arbitrary 64-bit constant if possible.571pub fn maybe_from_u64(value: u64) -> Option<MoveWideConst> {572let mask0 = 0x0000_0000_0000_ffffu64;573let mask1 = 0x0000_0000_ffff_0000u64;574let mask2 = 0x0000_ffff_0000_0000u64;575let mask3 = 0xffff_0000_0000_0000u64;576577if value == (value & mask0) {578return Some(MoveWideConst {579bits: (value & mask0) as u16,580shift: 0,581});582}583if value == (value & mask1) {584return Some(MoveWideConst {585bits: ((value >> 16) & mask0) as u16,586shift: 1,587});588}589if value == (value & mask2) {590return Some(MoveWideConst {591bits: ((value >> 32) & mask0) as u16,592shift: 2,593});594}595if value == (value & mask3) {596return Some(MoveWideConst {597bits: ((value >> 48) & mask0) as u16,598shift: 3,599});600}601None602}603604/// Create a `MoveWideCosnt` from a given shift, if possible.605pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> {606let shift_enc = shift / 16;607if shift_enc > 3 {608None609} else {610Some(MoveWideConst {611bits: imm,612shift: shift_enc,613})614}615}616617/// Create a zero immediate of this format.618pub fn zero() -> MoveWideConst {619MoveWideConst { bits: 0, shift: 0 }620}621}622623/// Advanced SIMD modified immediate as used by MOVI/MVNI.624#[derive(Clone, Copy, Debug, PartialEq)]625pub struct ASIMDMovModImm {626imm: u8,627shift: u8,628is_64bit: bool,629shift_ones: bool,630}631632impl ASIMDMovModImm {633/// Construct an ASIMDMovModImm from an arbitrary 64-bit constant, if possible.634/// Note that the bits in `value` outside of the range specified by `size` are635/// ignored; for example, in the case of `ScalarSize::Size8` all bits above the636/// lowest 8 are ignored.637pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm> {638match size {639ScalarSize::Size8 => Some(ASIMDMovModImm {640imm: value as u8,641shift: 0,642is_64bit: false,643shift_ones: false,644}),645ScalarSize::Size16 => {646let value = value as u16;647648if value >> 8 == 0 {649Some(ASIMDMovModImm {650imm: value as u8,651shift: 0,652is_64bit: false,653shift_ones: false,654})655} else if value as u8 == 0 {656Some(ASIMDMovModImm {657imm: (value >> 8) as u8,658shift: 8,659is_64bit: false,660shift_ones: false,661})662} else {663None664}665}666ScalarSize::Size32 => {667let value = value as u32;668669// Value is of the form 0x00MMFFFF.670if value & 0xFF00FFFF == 0x0000FFFF {671let imm = (value >> 16) as u8;672673Some(ASIMDMovModImm {674imm,675shift: 16,676is_64bit: false,677shift_ones: true,678})679// Value is of the form 0x0000MMFF.680} else if value & 0xFFFF00FF == 0x000000FF {681let imm = (value >> 8) as u8;682683Some(ASIMDMovModImm {684imm,685shift: 8,686is_64bit: false,687shift_ones: true,688})689} else {690// Of the 4 bytes, at most one is non-zero.691for shift in (0..32).step_by(8) {692if value & (0xFF << shift) == value {693return Some(ASIMDMovModImm {694imm: (value >> shift) as u8,695shift,696is_64bit: false,697shift_ones: false,698});699}700}701702None703}704}705ScalarSize::Size64 => {706let mut imm = 0u8;707708// Check if all bytes are either 0 or 0xFF.709for i in 0..8 {710let b = (value >> (i * 8)) as u8;711712if b == 0 || b == 0xFF {713imm |= (b & 1) << i;714} else {715return None;716}717}718719Some(ASIMDMovModImm {720imm,721shift: 0,722is_64bit: true,723shift_ones: false,724})725}726_ => None,727}728}729730/// Create a zero immediate of this format.731pub fn zero(size: ScalarSize) -> Self {732ASIMDMovModImm {733imm: 0,734shift: 0,735is_64bit: size == ScalarSize::Size64,736shift_ones: false,737}738}739740/// Returns the value that this immediate represents.741pub fn value(&self) -> (u8, u32, bool) {742(self.imm, self.shift as u32, self.shift_ones)743}744}745746/// Advanced SIMD modified immediate as used by the vector variant of FMOV.747#[derive(Clone, Copy, Debug, PartialEq)]748pub struct ASIMDFPModImm {749imm: u8,750size: ScalarSize,751}752753impl ASIMDFPModImm {754/// Construct an ASIMDFPModImm from an arbitrary 64-bit constant, if possible.755pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDFPModImm> {756// In all cases immediates are encoded as an 8-bit number 0b_abcdefgh;757// let `D` be the inverse of the digit `d`.758match size {759ScalarSize::Size16 => {760// In this case the representable immediates are 16-bit numbers of the form761// 0b_aBbb_cdef_gh00_0000.762let value = value as u16;763let b0_5 = (value >> 6) & 0b111111;764let b6 = (value >> 6) & (1 << 6);765let b7 = (value >> 8) & (1 << 7);766let imm = (b0_5 | b6 | b7) as u8;767768if value == Self::value16(imm) {769Some(ASIMDFPModImm { imm, size })770} else {771None772}773}774ScalarSize::Size32 => {775// In this case the representable immediates are 32-bit numbers of the form776// 0b_aBbb_bbbc_defg_h000 shifted to the left by 16.777let value = value as u32;778let b0_5 = (value >> 19) & 0b111111;779let b6 = (value >> 19) & (1 << 6);780let b7 = (value >> 24) & (1 << 7);781let imm = (b0_5 | b6 | b7) as u8;782783if value == Self::value32(imm) {784Some(ASIMDFPModImm { imm, size })785} else {786None787}788}789ScalarSize::Size64 => {790// In this case the representable immediates are 64-bit numbers of the form791// 0b_aBbb_bbbb_bbcd_efgh shifted to the left by 48.792let b0_5 = (value >> 48) & 0b111111;793let b6 = (value >> 48) & (1 << 6);794let b7 = (value >> 56) & (1 << 7);795let imm = (b0_5 | b6 | b7) as u8;796797if value == Self::value64(imm) {798Some(ASIMDFPModImm { imm, size })799} else {800None801}802}803_ => None,804}805}806807/// Returns bits ready for encoding.808pub fn enc_bits(&self) -> u8 {809self.imm810}811812/// Returns the 16-bit value that corresponds to an 8-bit encoding.813fn value16(imm: u8) -> u16 {814let imm = imm as u16;815let b0_5 = imm & 0b111111;816let b6 = (imm >> 6) & 1;817let b6_inv = b6 ^ 1;818let b7 = (imm >> 7) & 1;819820b0_5 << 6 | (b6 * 0b11) << 12 | b6_inv << 14 | b7 << 15821}822823/// Returns the 32-bit value that corresponds to an 8-bit encoding.824fn value32(imm: u8) -> u32 {825let imm = imm as u32;826let b0_5 = imm & 0b111111;827let b6 = (imm >> 6) & 1;828let b6_inv = b6 ^ 1;829let b7 = (imm >> 7) & 1;830831b0_5 << 19 | (b6 * 0b11111) << 25 | b6_inv << 30 | b7 << 31832}833834/// Returns the 64-bit value that corresponds to an 8-bit encoding.835fn value64(imm: u8) -> u64 {836let imm = imm as u64;837let b0_5 = imm & 0b111111;838let b6 = (imm >> 6) & 1;839let b6_inv = b6 ^ 1;840let b7 = (imm >> 7) & 1;841842b0_5 << 48 | (b6 * 0b11111111) << 54 | b6_inv << 62 | b7 << 63843}844}845846impl PrettyPrint for NZCV {847fn pretty_print(&self, _: u8) -> String {848let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c };849format!(850"#{}{}{}{}",851fmt('n', self.n),852fmt('z', self.z),853fmt('c', self.c),854fmt('v', self.v)855)856}857}858859impl PrettyPrint for UImm5 {860fn pretty_print(&self, _: u8) -> String {861format!("#{}", self.value)862}863}864865impl PrettyPrint for Imm12 {866fn pretty_print(&self, _: u8) -> String {867let shift = if self.shift12 { 12 } else { 0 };868let value = u32::from(self.bits) << shift;869format!("#{value}")870}871}872873impl PrettyPrint for SImm7Scaled {874fn pretty_print(&self, _: u8) -> String {875format!("#{}", self.value)876}877}878879impl PrettyPrint for FPULeftShiftImm {880fn pretty_print(&self, _: u8) -> String {881format!("#{}", self.amount)882}883}884885impl PrettyPrint for FPURightShiftImm {886fn pretty_print(&self, _: u8) -> String {887format!("#{}", self.amount)888}889}890891impl PrettyPrint for SImm9 {892fn pretty_print(&self, _: u8) -> String {893format!("#{}", self.value)894}895}896897impl PrettyPrint for UImm12Scaled {898fn pretty_print(&self, _: u8) -> String {899format!("#{}", self.value)900}901}902903impl PrettyPrint for ImmLogic {904fn pretty_print(&self, _: u8) -> String {905format!("#{}", self.value())906}907}908909impl PrettyPrint for ImmShift {910fn pretty_print(&self, _: u8) -> String {911format!("#{}", self.imm)912}913}914915impl PrettyPrint for MoveWideConst {916fn pretty_print(&self, _: u8) -> String {917if self.shift == 0 {918format!("#{}", self.bits)919} else {920format!("#{}, LSL #{}", self.bits, self.shift * 16)921}922}923}924925impl PrettyPrint for ASIMDMovModImm {926fn pretty_print(&self, _: u8) -> String {927if self.is_64bit {928debug_assert_eq!(self.shift, 0);929930let enc_imm = self.imm as i8;931let mut imm = 0u64;932933for i in 0..8 {934let b = (enc_imm >> i) & 1;935936imm |= (-b as u8 as u64) << (i * 8);937}938939format!("#{imm}")940} else if self.shift == 0 {941format!("#{}", self.imm)942} else {943let shift_type = if self.shift_ones { "MSL" } else { "LSL" };944format!("#{}, {} #{}", self.imm, shift_type, self.shift)945}946}947}948949impl PrettyPrint for ASIMDFPModImm {950fn pretty_print(&self, _: u8) -> String {951match self.size {952ScalarSize::Size16 => {953// FIXME(#8312): Use `f16` once it is stable.954// `value` will always be a normal number. Convert it to a `f32`.955let value: u32 = Self::value16(self.imm).into();956let sign = (value & 0x8000) << 16;957// Adjust the exponent for the difference between the `f16` exponent bias and the958// `f32` exponent bias.959let exponent = ((value & 0x7c00) + ((127 - 15) << 10)) << 13;960let significand = (value & 0x3ff) << 13;961format!("#{}", f32::from_bits(sign | exponent | significand))962}963ScalarSize::Size32 => format!("#{}", f32::from_bits(Self::value32(self.imm))),964ScalarSize::Size64 => format!("#{}", f64::from_bits(Self::value64(self.imm))),965_ => unreachable!(),966}967}968}969970#[cfg(test)]971mod test {972use super::*;973974#[test]975fn imm_logical_test() {976assert_eq!(None, ImmLogic::maybe_from_u64(0, I64));977assert_eq!(None, ImmLogic::maybe_from_u64(u64::max_value(), I64));978979assert_eq!(980Some(ImmLogic {981value: 1,982n: true,983r: 0,984s: 0,985size: OperandSize::Size64,986}),987ImmLogic::maybe_from_u64(1, I64)988);989990assert_eq!(991Some(ImmLogic {992value: 2,993n: true,994r: 63,995s: 0,996size: OperandSize::Size64,997}),998ImmLogic::maybe_from_u64(2, I64)999);10001001assert_eq!(None, ImmLogic::maybe_from_u64(5, I64));10021003assert_eq!(None, ImmLogic::maybe_from_u64(11, I64));10041005assert_eq!(1006Some(ImmLogic {1007value: 248,1008n: true,1009r: 61,1010s: 4,1011size: OperandSize::Size64,1012}),1013ImmLogic::maybe_from_u64(248, I64)1014);10151016assert_eq!(None, ImmLogic::maybe_from_u64(249, I64));10171018assert_eq!(1019Some(ImmLogic {1020value: 1920,1021n: true,1022r: 57,1023s: 3,1024size: OperandSize::Size64,1025}),1026ImmLogic::maybe_from_u64(1920, I64)1027);10281029assert_eq!(1030Some(ImmLogic {1031value: 0x7ffe,1032n: true,1033r: 63,1034s: 13,1035size: OperandSize::Size64,1036}),1037ImmLogic::maybe_from_u64(0x7ffe, I64)1038);10391040assert_eq!(1041Some(ImmLogic {1042value: 0x30000,1043n: true,1044r: 48,1045s: 1,1046size: OperandSize::Size64,1047}),1048ImmLogic::maybe_from_u64(0x30000, I64)1049);10501051assert_eq!(1052Some(ImmLogic {1053value: 0x100000,1054n: true,1055r: 44,1056s: 0,1057size: OperandSize::Size64,1058}),1059ImmLogic::maybe_from_u64(0x100000, I64)1060);10611062assert_eq!(1063Some(ImmLogic {1064value: u64::max_value() - 1,1065n: true,1066r: 63,1067s: 62,1068size: OperandSize::Size64,1069}),1070ImmLogic::maybe_from_u64(u64::max_value() - 1, I64)1071);10721073assert_eq!(1074Some(ImmLogic {1075value: 0xaaaaaaaaaaaaaaaa,1076n: false,1077r: 1,1078s: 60,1079size: OperandSize::Size64,1080}),1081ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64)1082);10831084assert_eq!(1085Some(ImmLogic {1086value: 0x8181818181818181,1087n: false,1088r: 1,1089s: 49,1090size: OperandSize::Size64,1091}),1092ImmLogic::maybe_from_u64(0x8181818181818181, I64)1093);10941095assert_eq!(1096Some(ImmLogic {1097value: 0xffc3ffc3ffc3ffc3,1098n: false,1099r: 10,1100s: 43,1101size: OperandSize::Size64,1102}),1103ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64)1104);11051106assert_eq!(1107Some(ImmLogic {1108value: 0x100000001,1109n: false,1110r: 0,1111s: 0,1112size: OperandSize::Size64,1113}),1114ImmLogic::maybe_from_u64(0x100000001, I64)1115);11161117assert_eq!(1118Some(ImmLogic {1119value: 0x1111111111111111,1120n: false,1121r: 0,1122s: 56,1123size: OperandSize::Size64,1124}),1125ImmLogic::maybe_from_u64(0x1111111111111111, I64)1126);11271128for n in 0..2 {1129let types = if n == 0 { vec![I64, I32] } else { vec![I64] };1130for s in 0..64 {1131for r in 0..64 {1132let imm = get_logical_imm(n, s, r);1133for &ty in &types {1134match ImmLogic::maybe_from_u64(imm, ty) {1135Some(ImmLogic { value, .. }) => {1136assert_eq!(imm, value);1137ImmLogic::maybe_from_u64(!value, ty).unwrap();1138}1139None => assert_eq!(0, imm),1140};1141}1142}1143}1144}1145}11461147// Repeat a value that has `width` bits, across a 64-bit value.1148fn repeat(value: u64, width: u64) -> u64 {1149let mut result = value & ((1 << width) - 1);1150let mut i = width;1151while i < 64 {1152result |= result << i;1153i *= 2;1154}1155result1156}11571158// Get the logical immediate, from the encoding N/R/S bits.1159fn get_logical_imm(n: u32, s: u32, r: u32) -> u64 {1160// An integer is constructed from the n, imm_s and imm_r bits according to1161// the following table:1162//1163// N imms immr size S R1164// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)1165// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)1166// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)1167// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)1168// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)1169// 0 11110s xxxxxr 2 UInt(s) UInt(r)1170// (s bits must not be all set)1171//1172// A pattern is constructed of size bits, where the least significant S+11173// bits are set. The pattern is rotated right by R, and repeated across a1174// 64-bit value.11751176if n == 1 {1177if s == 0x3f {1178return 0;1179}1180let bits = (1u64 << (s + 1)) - 1;1181bits.rotate_right(r)1182} else {1183if (s >> 1) == 0x1f {1184return 0;1185}1186let mut width = 0x20;1187while width >= 0x2 {1188if (s & width) == 0 {1189let mask = width - 1;1190if (s & mask) == mask {1191return 0;1192}1193let bits = (1u64 << ((s & mask) + 1)) - 1;1194return repeat(bits.rotate_right(r & mask), width.into());1195}1196width >>= 1;1197}1198unreachable!();1199}1200}12011202#[test]1203fn asimd_fp_mod_imm_test() {1204assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size32));1205assert_eq!(1206None,1207ASIMDFPModImm::maybe_from_u64(0.013671875_f32.to_bits() as u64, ScalarSize::Size32)1208);1209assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size64));1210assert_eq!(1211None,1212ASIMDFPModImm::maybe_from_u64(10000_f64.to_bits(), ScalarSize::Size64)1213);1214}12151216#[test]1217fn asimd_mov_mod_imm_test() {1218assert_eq!(1219None,1220ASIMDMovModImm::maybe_from_u64(513, ScalarSize::Size16)1221);1222assert_eq!(1223None,1224ASIMDMovModImm::maybe_from_u64(4278190335, ScalarSize::Size32)1225);1226assert_eq!(1227None,1228ASIMDMovModImm::maybe_from_u64(8388608, ScalarSize::Size64)1229);12301231assert_eq!(1232Some(ASIMDMovModImm {1233imm: 66,1234shift: 16,1235is_64bit: false,1236shift_ones: true,1237}),1238ASIMDMovModImm::maybe_from_u64(4390911, ScalarSize::Size32)1239);1240}1241}124212431244