diff --git a/cranelift/codegen/meta/src/cdsl/instructions.rs b/cranelift/codegen/meta/src/cdsl/instructions.rs index 0d27159cd9..464aa6e248 100644 --- a/cranelift/codegen/meta/src/cdsl/instructions.rs +++ b/cranelift/codegen/meta/src/cdsl/instructions.rs @@ -734,10 +734,10 @@ pub enum FormatPredicateKind { IsZero64BitFloat, /// Is the immediate format field member equal zero in all lanes? - IsAllZeroes128Bit, + IsAllZeroes, /// Does the immediate format field member have ones in all bits of all lanes? - IsAllOnes128Bit, + IsAllOnes, /// Has the value list (in member_name) the size specified in parameter? LengthEquals(usize), @@ -818,12 +818,12 @@ impl FormatPredicateNode { FormatPredicateKind::IsZero64BitFloat => { format!("predicates::is_zero_64_bit_float({})", self.member_name) } - FormatPredicateKind::IsAllZeroes128Bit => format!( - "predicates::is_all_zeroes_128_bit(func.dfg.constants.get({}))", + FormatPredicateKind::IsAllZeroes => format!( + "predicates::is_all_zeroes(func.dfg.constants.get({}))", self.member_name ), - FormatPredicateKind::IsAllOnes128Bit => format!( - "predicates::is_all_ones_128_bit(func.dfg.constants.get({}))", + FormatPredicateKind::IsAllOnes => format!( + "predicates::is_all_ones(func.dfg.constants.get({}))", self.member_name ), FormatPredicateKind::LengthEquals(num) => format!( @@ -1069,25 +1069,25 @@ impl InstructionPredicate { )) } - pub fn new_is_all_zeroes_128bit( + pub fn new_is_all_zeroes( format: &InstructionFormat, field_name: &'static str, ) -> InstructionPredicateNode { InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new( format, field_name, - FormatPredicateKind::IsAllZeroes128Bit, + FormatPredicateKind::IsAllZeroes, )) } - pub fn new_is_all_ones_128bit( + pub fn new_is_all_ones( format: &InstructionFormat, field_name: &'static str, ) -> InstructionPredicateNode { InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new( format, field_name, - FormatPredicateKind::IsAllOnes128Bit, + FormatPredicateKind::IsAllOnes, )) } diff --git a/cranelift/codegen/meta/src/isa/x86/encodings.rs b/cranelift/codegen/meta/src/isa/x86/encodings.rs index dcdc4d0196..a029e8170f 100644 --- a/cranelift/codegen/meta/src/isa/x86/encodings.rs +++ b/cranelift/codegen/meta/src/isa/x86/encodings.rs @@ -1848,14 +1848,14 @@ pub(crate) fn define<'defs>( let instruction = vconst.bind(vector(ty, sse_vector_size)); let is_zero_128bit = - InstructionPredicate::new_is_all_zeroes_128bit(f_unary_const, "constant_handle"); + InstructionPredicate::new_is_all_zeroes(f_unary_const, "constant_handle"); let template = rec_vconst_optimized.nonrex().opcodes(&PXOR); e.enc_32_64_func(instruction.clone(), template, |builder| { builder.inst_predicate(is_zero_128bit) }); let is_ones_128bit = - InstructionPredicate::new_is_all_ones_128bit(f_unary_const, "constant_handle"); + InstructionPredicate::new_is_all_ones(f_unary_const, "constant_handle"); let template = rec_vconst_optimized.nonrex().opcodes(&PCMPEQB); e.enc_32_64_func(instruction, template, |builder| { builder.inst_predicate(is_ones_128bit) diff --git a/cranelift/codegen/src/ir/constant.rs b/cranelift/codegen/src/ir/constant.rs index ff30d67734..bd69e60c6f 100644 --- a/cranelift/codegen/src/ir/constant.rs +++ b/cranelift/codegen/src/ir/constant.rs @@ -12,10 +12,65 @@ use crate::ir::Constant; use crate::HashMap; use alloc::collections::BTreeMap; use alloc::vec::Vec; +use core::fmt; +use core::slice::Iter; use cranelift_entity::EntityRef; -/// This type describes the actual constant data. -pub type ConstantData = Vec; +/// This type describes the actual constant data. Note that the bytes stored in this structure are +/// expected to be in little-endian order; this is due to ease-of-use when interacting with +/// WebAssembly values, which are [little-endian by design] +/// (https://github.com/WebAssembly/design/blob/master/Portability.md). +#[derive(Clone, Hash, Eq, PartialEq, Debug)] +pub struct ConstantData(Vec); + +impl From> for ConstantData { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl From<&[u8]> for ConstantData { + fn from(v: &[u8]) -> Self { + Self(v.to_vec()) + } +} + +impl ConstantData { + /// Return the number of bytes in the constant. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Iterate over the constant's bytes. + pub fn iter(&self) -> Iter { + self.0.iter() + } +} + +impl fmt::Display for ConstantData { + /// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f. + /// This function will flip the stored order of bytes--little-endian--to the more readable + /// big-endian ordering. Any zero bytes in high-order bytes will be discarded in the formatted + /// string. + /// + /// ``` + /// use cranelift_codegen::ir::ConstantData; + /// let data = ConstantData::from([3, 2, 1, 0, 0].as_ref()); // note the little-endian order + /// assert_eq!(data.to_string(), "0x010203"); + /// ``` + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "0x")?; + let mut bytes_written = 0; + for b in self.0.iter().rev().skip_while(|&&b| b == 0) { + write!(f, "{:02x}", b)?; + bytes_written += 1; + } + if bytes_written < 1 { + write!(f, "00")?; + } + Ok(()) + } +} /// This type describes an offset in bytes within a constant pool. pub type ConstantOffset = u32; @@ -79,7 +134,8 @@ impl ConstantPool { /// Insert constant data into the pool, returning a handle for later referencing; when constant /// data is inserted that is a duplicate of previous constant data, the existing handle will be /// returned. - pub fn insert(&mut self, constant_value: ConstantData) -> Constant { + pub fn insert(&mut self, constant_value: impl Into) -> Constant { + let constant_value = constant_value.into(); if self.values_to_handles.contains_key(&constant_value) { self.values_to_handles.get(&constant_value).unwrap().clone() } else { @@ -153,6 +209,7 @@ impl ConstantPool { #[cfg(test)] mod tests { use super::*; + use std::string::ToString; #[test] fn empty() { @@ -194,7 +251,7 @@ mod tests { sut.insert(vec![4, 5, 6]); sut.insert(vec![1, 2, 3]); let data = sut.iter().map(|(_, v)| v).collect::>(); - assert_eq!(data, vec![&vec![1, 2, 3], &vec![4, 5, 6]]); + assert_eq!(data, vec![&vec![1, 2, 3].into(), &vec![4, 5, 6].into()]); } #[test] @@ -202,7 +259,7 @@ mod tests { let mut sut = ConstantPool::new(); let data = vec![1, 2, 3]; let handle = sut.insert(data.clone()); - assert_eq!(sut.get(handle), &data); + assert_eq!(sut.get(handle), &data.into()); } #[test] @@ -228,4 +285,22 @@ mod tests { let a = sut.insert(vec![1]); sut.get_offset(a); // panics, set_offset should have been called } + + #[test] + fn display_constant_data() { + assert_eq!(ConstantData::from([0].as_ref()).to_string(), "0x00"); + assert_eq!(ConstantData::from([42].as_ref()).to_string(), "0x2a"); + assert_eq!( + ConstantData::from([3, 2, 1, 0].as_ref()).to_string(), + "0x010203" + ); + assert_eq!( + ConstantData::from(3735928559u32.to_le_bytes().as_ref()).to_string(), + "0xdeadbeef" + ); + assert_eq!( + ConstantData::from(0x0102030405060708u64.to_le_bytes().as_ref()).to_string(), + "0x0102030405060708" + ); + } } diff --git a/cranelift/codegen/src/isa/x86/enc_tables.rs b/cranelift/codegen/src/isa/x86/enc_tables.rs index 93adca0c25..64c0499d40 100644 --- a/cranelift/codegen/src/isa/x86/enc_tables.rs +++ b/cranelift/codegen/src/isa/x86/enc_tables.rs @@ -16,6 +16,7 @@ use crate::isa::RegUnit; use crate::isa::{self, TargetIsa}; use crate::predicates; use crate::regalloc::RegDiversions; +use std::vec::Vec; include!(concat!(env!("OUT_DIR"), "/encoding-x86.rs")); include!(concat!(env!("OUT_DIR"), "/legalize-x86.rs")); @@ -928,7 +929,7 @@ fn convert_shuffle( .clone(); if a == b { // PSHUFB the first argument (since it is the same as the second). - let constructed_mask = mask + let constructed_mask: Vec = mask .iter() // If the mask is greater than 15 it still may be referring to a lane in b. .map(|&b| if b > 15 { b.wrapping_sub(16) } else { b }) @@ -942,7 +943,8 @@ fn convert_shuffle( pos.func.dfg.replace(inst).x86_pshufb(a, mask_value); } else { // PSHUFB the first argument, placing zeroes for unused lanes. - let constructed_mask = mask.iter().cloned().map(zero_unknown_lane_index).collect(); + let constructed_mask: Vec = + mask.iter().cloned().map(zero_unknown_lane_index).collect(); let handle = pos.func.dfg.constants.insert(constructed_mask); // Move the built mask into another XMM register. let a_type = pos.func.dfg.value_type(a); @@ -951,7 +953,7 @@ fn convert_shuffle( let shuffled_first_arg = pos.ins().x86_pshufb(a, mask_value); // PSHUFB the second argument, placing zeroes for unused lanes. - let constructed_mask = mask + let constructed_mask: Vec = mask .iter() .map(|b| b.wrapping_sub(16)) .map(zero_unknown_lane_index) diff --git a/cranelift/codegen/src/predicates.rs b/cranelift/codegen/src/predicates.rs index 16672b145b..374e2bbbff 100644 --- a/cranelift/codegen/src/predicates.rs +++ b/cranelift/codegen/src/predicates.rs @@ -10,6 +10,7 @@ //! dead code warning. use crate::ir; +use crate::ir::ConstantData; /// Check that an integer value is zero. #[allow(dead_code)] @@ -33,14 +34,14 @@ pub fn is_zero_32_bit_float>(x: T) -> bool { /// Check that a 128-bit vector contains all zeroes. #[allow(dead_code)] -pub fn is_all_zeroes_128_bit<'b, T: PartialEq<&'b [u8; 16]>>(x: T) -> bool { - x.eq(&&[0; 16]) +pub fn is_all_zeroes(x: &ConstantData) -> bool { + x.iter().all(|&f| f == 0) } /// Check that a 128-bit vector contains all ones. #[allow(dead_code)] -pub fn is_all_ones_128_bit<'b, T: PartialEq<&'b [u8; 16]>>(x: T) -> bool { - x.eq(&&[0xff; 16]) +pub fn is_all_ones(x: &ConstantData) -> bool { + x.iter().all(|&f| f == 0xff) } /// Check that `x` is the same as `y`. @@ -123,17 +124,17 @@ mod tests { } #[test] - fn is_all_zeroes() { - assert!(is_all_zeroes_128_bit(&[0; 16])); - assert!(is_all_zeroes_128_bit(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ])); - assert!(!is_all_zeroes_128_bit(&[1; 16])); + fn check_is_all_zeroes() { + assert!(is_all_zeroes(&[0; 16].as_ref().into())); + assert!(is_all_zeroes( + &vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].into() + )); + assert!(!is_all_zeroes(&[1; 16].as_ref().into())); } #[test] - fn is_all_ones() { - assert!(!is_all_ones_128_bit(&[0; 16])); - assert!(is_all_ones_128_bit(&[0xff; 16])); + fn check_is_all_ones() { + assert!(!is_all_ones(&[0; 16].as_ref().into())); + assert!(is_all_ones(&[0xff; 16].as_ref().into())); } } diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs index ae093f3919..910d1b988e 100644 --- a/cranelift/codegen/src/write.rs +++ b/cranelift/codegen/src/write.rs @@ -508,9 +508,8 @@ pub fn write_operands( UnaryConst { constant_handle, .. } => { - let data = dfg.constants.get(constant_handle); - let v128 = V128Imm::from(&data[..]); - write!(w, " {}", v128) + let constant_data = dfg.constants.get(constant_handle); + write!(w, " {}", constant_data) } Shuffle { mask, args, .. } => { let data = dfg.immediates.get(mask).expect(