Make ConstantData a container for any-size constant values
Previously, ConstantData was a type alias for `Vec<u8>` which prevented it from having an implementation; this meant that `V128Imm` and `&[u8; 16]` were used in places that otherwise could have accepted types of different byte lengths.
This commit is contained in:
@@ -734,10 +734,10 @@ pub enum FormatPredicateKind {
|
|||||||
IsZero64BitFloat,
|
IsZero64BitFloat,
|
||||||
|
|
||||||
/// Is the immediate format field member equal zero in all lanes?
|
/// 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?
|
/// 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?
|
/// Has the value list (in member_name) the size specified in parameter?
|
||||||
LengthEquals(usize),
|
LengthEquals(usize),
|
||||||
@@ -818,12 +818,12 @@ impl FormatPredicateNode {
|
|||||||
FormatPredicateKind::IsZero64BitFloat => {
|
FormatPredicateKind::IsZero64BitFloat => {
|
||||||
format!("predicates::is_zero_64_bit_float({})", self.member_name)
|
format!("predicates::is_zero_64_bit_float({})", self.member_name)
|
||||||
}
|
}
|
||||||
FormatPredicateKind::IsAllZeroes128Bit => format!(
|
FormatPredicateKind::IsAllZeroes => format!(
|
||||||
"predicates::is_all_zeroes_128_bit(func.dfg.constants.get({}))",
|
"predicates::is_all_zeroes(func.dfg.constants.get({}))",
|
||||||
self.member_name
|
self.member_name
|
||||||
),
|
),
|
||||||
FormatPredicateKind::IsAllOnes128Bit => format!(
|
FormatPredicateKind::IsAllOnes => format!(
|
||||||
"predicates::is_all_ones_128_bit(func.dfg.constants.get({}))",
|
"predicates::is_all_ones(func.dfg.constants.get({}))",
|
||||||
self.member_name
|
self.member_name
|
||||||
),
|
),
|
||||||
FormatPredicateKind::LengthEquals(num) => format!(
|
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,
|
format: &InstructionFormat,
|
||||||
field_name: &'static str,
|
field_name: &'static str,
|
||||||
) -> InstructionPredicateNode {
|
) -> InstructionPredicateNode {
|
||||||
InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new(
|
InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new(
|
||||||
format,
|
format,
|
||||||
field_name,
|
field_name,
|
||||||
FormatPredicateKind::IsAllZeroes128Bit,
|
FormatPredicateKind::IsAllZeroes,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_is_all_ones_128bit(
|
pub fn new_is_all_ones(
|
||||||
format: &InstructionFormat,
|
format: &InstructionFormat,
|
||||||
field_name: &'static str,
|
field_name: &'static str,
|
||||||
) -> InstructionPredicateNode {
|
) -> InstructionPredicateNode {
|
||||||
InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new(
|
InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new(
|
||||||
format,
|
format,
|
||||||
field_name,
|
field_name,
|
||||||
FormatPredicateKind::IsAllOnes128Bit,
|
FormatPredicateKind::IsAllOnes,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1848,14 +1848,14 @@ pub(crate) fn define<'defs>(
|
|||||||
let instruction = vconst.bind(vector(ty, sse_vector_size));
|
let instruction = vconst.bind(vector(ty, sse_vector_size));
|
||||||
|
|
||||||
let is_zero_128bit =
|
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);
|
let template = rec_vconst_optimized.nonrex().opcodes(&PXOR);
|
||||||
e.enc_32_64_func(instruction.clone(), template, |builder| {
|
e.enc_32_64_func(instruction.clone(), template, |builder| {
|
||||||
builder.inst_predicate(is_zero_128bit)
|
builder.inst_predicate(is_zero_128bit)
|
||||||
});
|
});
|
||||||
|
|
||||||
let is_ones_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);
|
let template = rec_vconst_optimized.nonrex().opcodes(&PCMPEQB);
|
||||||
e.enc_32_64_func(instruction, template, |builder| {
|
e.enc_32_64_func(instruction, template, |builder| {
|
||||||
builder.inst_predicate(is_ones_128bit)
|
builder.inst_predicate(is_ones_128bit)
|
||||||
|
|||||||
@@ -12,10 +12,65 @@ use crate::ir::Constant;
|
|||||||
use crate::HashMap;
|
use crate::HashMap;
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use core::fmt;
|
||||||
|
use core::slice::Iter;
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
|
|
||||||
/// This type describes the actual constant data.
|
/// This type describes the actual constant data. Note that the bytes stored in this structure are
|
||||||
pub type ConstantData = Vec<u8>;
|
/// 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<u8>);
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for ConstantData {
|
||||||
|
fn from(v: Vec<u8>) -> 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<u8> {
|
||||||
|
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.
|
/// This type describes an offset in bytes within a constant pool.
|
||||||
pub type ConstantOffset = u32;
|
pub type ConstantOffset = u32;
|
||||||
@@ -79,7 +134,8 @@ impl ConstantPool {
|
|||||||
/// Insert constant data into the pool, returning a handle for later referencing; when constant
|
/// 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
|
/// data is inserted that is a duplicate of previous constant data, the existing handle will be
|
||||||
/// returned.
|
/// returned.
|
||||||
pub fn insert(&mut self, constant_value: ConstantData) -> Constant {
|
pub fn insert(&mut self, constant_value: impl Into<ConstantData>) -> Constant {
|
||||||
|
let constant_value = constant_value.into();
|
||||||
if self.values_to_handles.contains_key(&constant_value) {
|
if self.values_to_handles.contains_key(&constant_value) {
|
||||||
self.values_to_handles.get(&constant_value).unwrap().clone()
|
self.values_to_handles.get(&constant_value).unwrap().clone()
|
||||||
} else {
|
} else {
|
||||||
@@ -153,6 +209,7 @@ impl ConstantPool {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::string::ToString;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty() {
|
fn empty() {
|
||||||
@@ -194,7 +251,7 @@ mod tests {
|
|||||||
sut.insert(vec![4, 5, 6]);
|
sut.insert(vec![4, 5, 6]);
|
||||||
sut.insert(vec![1, 2, 3]);
|
sut.insert(vec![1, 2, 3]);
|
||||||
let data = sut.iter().map(|(_, v)| v).collect::<Vec<&ConstantData>>();
|
let data = sut.iter().map(|(_, v)| v).collect::<Vec<&ConstantData>>();
|
||||||
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]
|
#[test]
|
||||||
@@ -202,7 +259,7 @@ mod tests {
|
|||||||
let mut sut = ConstantPool::new();
|
let mut sut = ConstantPool::new();
|
||||||
let data = vec![1, 2, 3];
|
let data = vec![1, 2, 3];
|
||||||
let handle = sut.insert(data.clone());
|
let handle = sut.insert(data.clone());
|
||||||
assert_eq!(sut.get(handle), &data);
|
assert_eq!(sut.get(handle), &data.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -228,4 +285,22 @@ mod tests {
|
|||||||
let a = sut.insert(vec![1]);
|
let a = sut.insert(vec![1]);
|
||||||
sut.get_offset(a); // panics, set_offset should have been called
|
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"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::isa::RegUnit;
|
|||||||
use crate::isa::{self, TargetIsa};
|
use crate::isa::{self, TargetIsa};
|
||||||
use crate::predicates;
|
use crate::predicates;
|
||||||
use crate::regalloc::RegDiversions;
|
use crate::regalloc::RegDiversions;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/encoding-x86.rs"));
|
include!(concat!(env!("OUT_DIR"), "/encoding-x86.rs"));
|
||||||
include!(concat!(env!("OUT_DIR"), "/legalize-x86.rs"));
|
include!(concat!(env!("OUT_DIR"), "/legalize-x86.rs"));
|
||||||
@@ -928,7 +929,7 @@ fn convert_shuffle(
|
|||||||
.clone();
|
.clone();
|
||||||
if a == b {
|
if a == b {
|
||||||
// PSHUFB the first argument (since it is the same as the second).
|
// PSHUFB the first argument (since it is the same as the second).
|
||||||
let constructed_mask = mask
|
let constructed_mask: Vec<u8> = mask
|
||||||
.iter()
|
.iter()
|
||||||
// If the mask is greater than 15 it still may be referring to a lane in b.
|
// 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 })
|
.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);
|
pos.func.dfg.replace(inst).x86_pshufb(a, mask_value);
|
||||||
} else {
|
} else {
|
||||||
// PSHUFB the first argument, placing zeroes for unused lanes.
|
// 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<u8> =
|
||||||
|
mask.iter().cloned().map(zero_unknown_lane_index).collect();
|
||||||
let handle = pos.func.dfg.constants.insert(constructed_mask);
|
let handle = pos.func.dfg.constants.insert(constructed_mask);
|
||||||
// Move the built mask into another XMM register.
|
// Move the built mask into another XMM register.
|
||||||
let a_type = pos.func.dfg.value_type(a);
|
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);
|
let shuffled_first_arg = pos.ins().x86_pshufb(a, mask_value);
|
||||||
|
|
||||||
// PSHUFB the second argument, placing zeroes for unused lanes.
|
// PSHUFB the second argument, placing zeroes for unused lanes.
|
||||||
let constructed_mask = mask
|
let constructed_mask: Vec<u8> = mask
|
||||||
.iter()
|
.iter()
|
||||||
.map(|b| b.wrapping_sub(16))
|
.map(|b| b.wrapping_sub(16))
|
||||||
.map(zero_unknown_lane_index)
|
.map(zero_unknown_lane_index)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
//! dead code warning.
|
//! dead code warning.
|
||||||
|
|
||||||
use crate::ir;
|
use crate::ir;
|
||||||
|
use crate::ir::ConstantData;
|
||||||
|
|
||||||
/// Check that an integer value is zero.
|
/// Check that an integer value is zero.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -33,14 +34,14 @@ pub fn is_zero_32_bit_float<T: Into<ir::immediates::Ieee32>>(x: T) -> bool {
|
|||||||
|
|
||||||
/// Check that a 128-bit vector contains all zeroes.
|
/// Check that a 128-bit vector contains all zeroes.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn is_all_zeroes_128_bit<'b, T: PartialEq<&'b [u8; 16]>>(x: T) -> bool {
|
pub fn is_all_zeroes(x: &ConstantData) -> bool {
|
||||||
x.eq(&&[0; 16])
|
x.iter().all(|&f| f == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that a 128-bit vector contains all ones.
|
/// Check that a 128-bit vector contains all ones.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn is_all_ones_128_bit<'b, T: PartialEq<&'b [u8; 16]>>(x: T) -> bool {
|
pub fn is_all_ones(x: &ConstantData) -> bool {
|
||||||
x.eq(&&[0xff; 16])
|
x.iter().all(|&f| f == 0xff)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that `x` is the same as `y`.
|
/// Check that `x` is the same as `y`.
|
||||||
@@ -123,17 +124,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_all_zeroes() {
|
fn check_is_all_zeroes() {
|
||||||
assert!(is_all_zeroes_128_bit(&[0; 16]));
|
assert!(is_all_zeroes(&[0; 16].as_ref().into()));
|
||||||
assert!(is_all_zeroes_128_bit(vec![
|
assert!(is_all_zeroes(
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
&vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].into()
|
||||||
]));
|
));
|
||||||
assert!(!is_all_zeroes_128_bit(&[1; 16]));
|
assert!(!is_all_zeroes(&[1; 16].as_ref().into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_all_ones() {
|
fn check_is_all_ones() {
|
||||||
assert!(!is_all_ones_128_bit(&[0; 16]));
|
assert!(!is_all_ones(&[0; 16].as_ref().into()));
|
||||||
assert!(is_all_ones_128_bit(&[0xff; 16]));
|
assert!(is_all_ones(&[0xff; 16].as_ref().into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -508,9 +508,8 @@ pub fn write_operands(
|
|||||||
UnaryConst {
|
UnaryConst {
|
||||||
constant_handle, ..
|
constant_handle, ..
|
||||||
} => {
|
} => {
|
||||||
let data = dfg.constants.get(constant_handle);
|
let constant_data = dfg.constants.get(constant_handle);
|
||||||
let v128 = V128Imm::from(&data[..]);
|
write!(w, " {}", constant_data)
|
||||||
write!(w, " {}", v128)
|
|
||||||
}
|
}
|
||||||
Shuffle { mask, args, .. } => {
|
Shuffle { mask, args, .. } => {
|
||||||
let data = dfg.immediates.get(mask).expect(
|
let data = dfg.immediates.get(mask).expect(
|
||||||
|
|||||||
Reference in New Issue
Block a user