Replace V128Imm functionality with ConstantData

This moves most original uses of V128Imm (e.g. in parsing) to ConstantData and shifts the unit tests from V128Imm to ConstantData.
This commit is contained in:
Andrew Brown
2019-10-01 20:13:17 -07:00
parent 1600dba634
commit a03f905d08
7 changed files with 263 additions and 272 deletions

View File

@@ -8,19 +8,21 @@
//! - ensuring alignment of constants within the pool, //! - ensuring alignment of constants within the pool,
//! - bucketing constants by size. //! - bucketing constants by size.
use crate::ir::immediates::{IntoBytes, V128Imm};
use crate::ir::Constant; 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::fmt;
use core::slice::Iter; use core::slice::Iter;
use core::str::{from_utf8, FromStr};
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
/// This type describes the actual constant data. Note that the bytes stored in this structure are /// 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 /// 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] /// WebAssembly values, which are [little-endian by design]
/// (https://github.com/WebAssembly/design/blob/master/Portability.md). /// (https://github.com/WebAssembly/design/blob/master/Portability.md).
#[derive(Clone, Hash, Eq, PartialEq, Debug)] #[derive(Clone, Hash, Eq, PartialEq, Debug, Default)]
pub struct ConstantData(Vec<u8>); pub struct ConstantData(Vec<u8>);
impl From<Vec<u8>> for ConstantData { impl From<Vec<u8>> for ConstantData {
@@ -35,16 +37,47 @@ impl From<&[u8]> for ConstantData {
} }
} }
impl From<V128Imm> for ConstantData {
fn from(v: V128Imm) -> Self {
Self(v.to_vec())
}
}
impl ConstantData { impl ConstantData {
/// Return the number of bytes in the constant. /// Return the number of bytes in the constant.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }
/// Convert the data to a vector.
pub fn to_vec(self) -> Vec<u8> {
self.0
}
/// Iterate over the constant's bytes. /// Iterate over the constant's bytes.
pub fn iter(&self) -> Iter<u8> { pub fn iter(&self) -> Iter<u8> {
self.0.iter() self.0.iter()
} }
/// Add new bytes to the constant data.
pub fn append(mut self, bytes: impl IntoBytes) -> Self {
let mut to_add = bytes.into_bytes();
self.0.append(&mut to_add);
self
}
/// Expand the size of the constant data to `expected_size` number of bytes by adding zeroes
/// in the high-order byte slots.
pub fn expand_to(mut self, expected_size: usize) -> Self {
if self.len() > expected_size {
panic!(
"The constant data is already expanded beyond {} bytes",
expected_size
)
}
self.0.resize(expected_size, 0);
self
}
} }
impl fmt::Display for ConstantData { impl fmt::Display for ConstantData {
@@ -72,6 +105,50 @@ impl fmt::Display for ConstantData {
} }
} }
impl FromStr for ConstantData {
type Err = &'static str;
/// Parse a hexadecimal string to `ConstantData`. This is the inverse of [ConstantData::fmt]
/// (cranelift_codegen::ir::ConstantData::fmt).
///
/// ```
/// use cranelift_codegen::ir::ConstantData;
/// let c: ConstantData = "0x000102".parse().unwrap();
/// assert_eq!(c.to_vec(), [2, 1, 0]);
/// ```
fn from_str(s: &str) -> Result<Self, &'static str> {
if s.len() <= 2 || &s[0..2] != "0x" {
return Err("Expected a hexadecimal string, e.g. 0x1234");
}
// clean and check the string
let cleaned: Vec<u8> = s[2..]
.as_bytes()
.iter()
.filter(|&&b| b as char != '_')
.cloned()
.collect(); // remove 0x prefix and any intervening _ characters
if cleaned.len() == 0 {
Err("Hexadecimal string must have some digits")
} else if cleaned.len() % 2 != 0 {
Err("Hexadecimal string must have an even number of digits")
} else if cleaned.len() > 32 {
Err("Hexadecimal string has too many digits to fit in a 128-bit vector")
} else {
let mut buffer = Vec::with_capacity((s.len() - 2) / 2);
for i in (0..cleaned.len()).step_by(2) {
let pair = from_utf8(&cleaned[i..i + 2])
.or_else(|_| Err("Unable to parse hexadecimal pair as UTF-8"))?;
let byte = u8::from_str_radix(pair, 16)
.or_else(|_| Err("Unable to parse as hexadecimal"))?;
buffer.insert(0, byte);
}
Ok(ConstantData(buffer))
}
}
}
/// 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;
@@ -303,4 +380,105 @@ mod tests {
"0x0102030405060708" "0x0102030405060708"
); );
} }
#[test]
fn iterate_over_constant_data() {
let c = ConstantData::from([1, 2, 3].as_ref());
let mut iter = c.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), None);
}
#[test]
fn add_to_constant_data() {
let d = ConstantData::from([1, 2].as_ref());
let e = d.append(i16::from(3u8));
assert_eq!(e.to_vec(), vec![1, 2, 3, 0])
}
#[test]
fn extend_constant_data() {
let d = ConstantData::from([1, 2].as_ref());
assert_eq!(d.expand_to(4).to_vec(), vec![1, 2, 0, 0])
}
#[test]
#[should_panic]
fn extend_constant_data_to_invalid_length() {
ConstantData::from([1, 2].as_ref()).expand_to(1);
}
#[test]
fn parse_constant_data_and_restringify() {
// Verify that parsing of `from` succeeds and stringifies to `to`.
fn parse_ok(from: &str, to: &str) {
let parsed = from.parse::<ConstantData>().unwrap();
assert_eq!(parsed.to_string(), to);
}
// Verify that parsing of `from` fails with `error_msg`.
fn parse_err(from: &str, error_msg: &str) {
let parsed = from.parse::<ConstantData>();
assert!(
parsed.is_err(),
"Expected a parse error but parsing succeeded: {}",
from
);
assert_eq!(parsed.err().unwrap(), error_msg);
}
parse_ok("0x00", "0x00");
parse_ok("0x00000042", "0x42");
parse_ok(
"0x0102030405060708090a0b0c0d0e0f00",
"0x0102030405060708090a0b0c0d0e0f00",
);
parse_ok("0x_0000_0043_21", "0x4321");
parse_err("", "Expected a hexadecimal string, e.g. 0x1234");
parse_err("0x", "Expected a hexadecimal string, e.g. 0x1234");
parse_err(
"0x042",
"Hexadecimal string must have an even number of digits",
);
parse_err(
"0x00000000000000000000000000000000000000000000000000",
"Hexadecimal string has too many digits to fit in a 128-bit vector",
);
parse_err("0xrstu", "Unable to parse as hexadecimal");
parse_err("0x__", "Hexadecimal string must have some digits");
}
#[test]
fn verify_stored_bytes_in_constant_data() {
assert_eq!("0x01".parse::<ConstantData>().unwrap().to_vec(), [1]);
assert_eq!(ConstantData::from([1, 0].as_ref()).0, [1, 0]);
assert_eq!(ConstantData::from(vec![1, 0, 0, 0]).0, [1, 0, 0, 0]);
}
#[test]
fn check_constant_data_endianness_as_uimm128() {
fn parse_to_uimm128(from: &str) -> Vec<u8> {
from.parse::<ConstantData>().unwrap().expand_to(16).to_vec()
}
assert_eq!(
parse_to_uimm128("0x42"),
[0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
parse_to_uimm128("0x00"),
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
parse_to_uimm128("0x12345678"),
[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
parse_to_uimm128("0x1234_5678"),
[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
} }

View File

@@ -5,7 +5,7 @@ use crate::ir;
use crate::ir::builder::ReplaceBuilder; use crate::ir::builder::ReplaceBuilder;
use crate::ir::extfunc::ExtFuncData; use crate::ir::extfunc::ExtFuncData;
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionData}; use crate::ir::instructions::{BranchInfo, CallInfo, InstructionData};
use crate::ir::{types, ConstantPool, Immediate}; use crate::ir::{types, ConstantData, ConstantPool, Immediate};
use crate::ir::{ use crate::ir::{
Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueLabelAssignments, ValueList, Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueLabelAssignments, ValueList,
ValueListPool, ValueListPool,
@@ -14,7 +14,6 @@ use crate::isa::TargetIsa;
use crate::packed_option::ReservedValue; use crate::packed_option::ReservedValue;
use crate::write::write_operands; use crate::write::write_operands;
use crate::HashMap; use crate::HashMap;
use alloc::vec::Vec;
use core::fmt; use core::fmt;
use core::iter; use core::iter;
use core::mem; use core::mem;
@@ -73,7 +72,7 @@ pub struct DataFlowGraph {
pub constants: ConstantPool, pub constants: ConstantPool,
/// Stores large immediates that otherwise will not fit on InstructionData /// Stores large immediates that otherwise will not fit on InstructionData
pub immediates: PrimaryMap<Immediate, Vec<u8>>, pub immediates: PrimaryMap<Immediate, ConstantData>,
} }
impl DataFlowGraph { impl DataFlowGraph {

View File

@@ -6,13 +6,13 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt::{self, Display, Formatter}; use core::fmt::{self, Display, Formatter};
use core::iter::FromIterator; use core::str::FromStr;
use core::str::{from_utf8, FromStr};
use core::{i32, u32}; use core::{i32, u32};
/// Convert a type into a vector of bytes; all implementors in this file must use little-endian /// Convert a type into a vector of bytes; all implementors in this file must use little-endian
/// orderings of bytes to match WebAssembly's little-endianness. /// orderings of bytes to match WebAssembly's little-endianness.
trait IntoBytes { pub trait IntoBytes {
/// Return the little-endian byte representation of the implementing type.
fn into_bytes(self) -> Vec<u8>; fn into_bytes(self) -> Vec<u8>;
} }
@@ -34,6 +34,12 @@ impl IntoBytes for i32 {
} }
} }
impl IntoBytes for Vec<u8> {
fn into_bytes(self) -> Vec<u8> {
self
}
}
/// 64-bit immediate signed integer operand. /// 64-bit immediate signed integer operand.
/// ///
/// An `Imm64` operand can also be used to represent immediate values of smaller integer types by /// An `Imm64` operand can also be used to represent immediate values of smaller integer types by
@@ -318,34 +324,6 @@ impl V128Imm {
} }
} }
impl Display for V128Imm {
// Print a 128-bit vector in hexadecimal, e.g. 0x000102030405060708090a0b0c0d0e0f.
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "0x")?;
let mut anything_written = false;
for &b in self.0.iter().rev() {
if b == 0 && !anything_written {
continue;
} else {
anything_written = true;
write!(f, "{:02x}", b)?;
}
}
if !anything_written {
write!(f, "00")?;
}
Ok(())
}
}
impl From<u64> for V128Imm {
fn from(x: u64) -> Self {
let mut buffer: [u8; 16] = [0; 16]; // zero-fill
(0..8).for_each(|byte| buffer[byte] = (x >> (byte as u64 * 8) & 0xff) as u8); // insert each byte from the u64 into v in little-endian order
V128Imm(buffer)
}
}
impl From<&[u8]> for V128Imm { impl From<&[u8]> for V128Imm {
fn from(slice: &[u8]) -> Self { fn from(slice: &[u8]) -> Self {
assert_eq!(slice.len(), 16); assert_eq!(slice.len(), 16);
@@ -355,92 +333,6 @@ impl From<&[u8]> for V128Imm {
} }
} }
impl FromStr for V128Imm {
type Err = &'static str;
// parse a 128-bit vector from a hexadecimal string, formatted as above
fn from_str(s: &str) -> Result<Self, &'static str> {
if s.len() <= 2 || &s[0..2] != "0x" {
Err("Expected a hexadecimal string, e.g. 0x1234")
} else {
// clean and check the string
let cleaned: Vec<u8> = s[2..]
.as_bytes()
.iter()
.filter(|&&b| b as char != '_')
.cloned()
.collect(); // remove 0x prefix and any intervening _ characters
if cleaned.len() == 0 {
Err("Hexadecimal string must have some digits")
} else if cleaned.len() % 2 != 0 {
Err("Hexadecimal string must have an even number of digits")
} else if cleaned.len() > 32 {
Err("Hexadecimal string has too many digits to fit in a 128-bit vector")
} else {
let mut buffer = [0; 16]; // zero-fill the buffer
let mut position = cleaned.len() / 2 - 1; // since Uimm128 is little-endian but the string is not, we write from back to front but must start at the highest position required by the string
for i in (0..cleaned.len()).step_by(2) {
let pair = from_utf8(&cleaned[i..i + 2])
.or_else(|_| Err("Unable to parse hexadecimal pair as UTF-8"))?;
let byte = u8::from_str_radix(pair, 16)
.or_else(|_| Err("Unable to parse as hexadecimal"))?;
buffer[position] = byte;
position = position.wrapping_sub(1); // should only wrap on the last iteration
}
Ok(V128Imm(buffer))
}
}
}
}
/// Implement a way to convert an iterator of immediates to a Uimm128:
/// - this expects the items in reverse order (e.g. last lane first) which is the natural output of pushing items into a vector
/// - this may not fully consume the iterator or may fail if it cannot take the expected number of items
/// - this requires the input type (i.e. $ty) to implement ToBytes
macro_rules! construct_uimm128_from_iterator_of {
( $ty:ident, $lanes:expr ) => {
impl FromIterator<$ty> for V128Imm {
fn from_iter<T: IntoIterator<Item = $ty>>(iter: T) -> Self {
let mut buffer: [u8; 16] = [0; 16];
iter.into_iter()
.take($lanes)
.map(|f| f.into_bytes())
.flat_map(|b| b)
.enumerate()
.for_each(|(i, b)| buffer[i] = b);
V128Imm(buffer)
}
}
};
}
/// Special case for booleans since we have to decide the bit-width based on the number of items
impl FromIterator<bool> for V128Imm {
fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
let bools = Vec::from_iter(iter);
let count = bools.len();
assert!(count > 0 && count <= 16); // ensure we don't have too many booleans
assert_eq!(count & (count - 1), 0); // ensure count is a power of two, see https://stackoverflow.com/questions/600293
let mut buffer: [u8; 16] = [0; 16];
let step = 16 / count;
bools
.iter()
.enumerate()
.map(|(i, &b)| (i * step, if b { 1 } else { 0 }))
.for_each(|(i, b)| buffer[i] = b);
V128Imm(buffer)
}
}
construct_uimm128_from_iterator_of!(u8, 16);
construct_uimm128_from_iterator_of!(i16, 8);
construct_uimm128_from_iterator_of!(i32, 4);
construct_uimm128_from_iterator_of!(Ieee32, 4);
construct_uimm128_from_iterator_of!(Imm64, 2);
construct_uimm128_from_iterator_of!(Ieee64, 2);
/// 32-bit signed immediate offset. /// 32-bit signed immediate offset.
/// ///
/// This is used to encode an immediate offset for load/store instructions. All supported ISAs have /// This is used to encode an immediate offset for load/store instructions. All supported ISAs have
@@ -1074,117 +966,6 @@ mod tests {
parse_err::<Uimm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits"); parse_err::<Uimm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
} }
#[test]
fn format_uimm128() {
assert_eq!(V128Imm::from(0).to_string(), "0x00");
assert_eq!(V128Imm::from(42).to_string(), "0x2a");
assert_eq!(V128Imm::from(3735928559).to_string(), "0xdeadbeef");
assert_eq!(
V128Imm::from(0x0102030405060708).to_string(),
"0x0102030405060708"
);
}
#[test]
fn parse_uimm128() {
parse_ok::<V128Imm>("0x00", "0x00");
parse_ok::<V128Imm>("0x00000042", "0x42");
parse_ok::<V128Imm>(
"0x0102030405060708090a0b0c0d0e0f00",
"0x0102030405060708090a0b0c0d0e0f00",
);
parse_ok::<V128Imm>("0x_0000_0043_21", "0x4321");
parse_err::<V128Imm>("", "Expected a hexadecimal string, e.g. 0x1234");
parse_err::<V128Imm>("0x", "Expected a hexadecimal string, e.g. 0x1234");
parse_err::<V128Imm>(
"0x042",
"Hexadecimal string must have an even number of digits",
);
parse_err::<V128Imm>(
"0x00000000000000000000000000000000000000000000000000",
"Hexadecimal string has too many digits to fit in a 128-bit vector",
);
parse_err::<V128Imm>("0xrstu", "Unable to parse as hexadecimal");
parse_err::<V128Imm>("0x__", "Hexadecimal string must have some digits");
}
#[test]
fn uimm128_equivalence() {
assert_eq!(
"0x01".parse::<V128Imm>().unwrap().0,
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
V128Imm::from_iter(vec![1, 0, 0, 0]).0,
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
V128Imm::from(1).0,
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[test]
fn uimm128_endianness() {
assert_eq!(
"0x42".parse::<V128Imm>().unwrap().0,
[0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
"0x00".parse::<V128Imm>().unwrap().0,
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
"0x12345678".parse::<V128Imm>().unwrap().0,
[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
"0x1234_5678".parse::<V128Imm>().unwrap().0,
[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[test]
fn uimm128_from_iter() {
assert_eq!(
V128Imm::from_iter(vec![4, 3, 2, 1]).0,
[4, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0]
);
assert_eq!(
V128Imm::from_iter(vec![false, true]).0,
[/* false */ 0, 0, 0, 0, 0, 0, 0, 0, /* true */ 1, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
V128Imm::from_iter(vec![false, true, false, true, false, true, false, true]).0,
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0]
);
#[allow(trivial_numeric_casts)]
let u8s = vec![
1 as u8, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0,
];
assert_eq!(
V128Imm::from_iter(u8s).0,
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]
);
#[allow(trivial_numeric_casts)]
let ieee32s: Vec<Ieee32> = vec![32.4 as f32, 0.0, 1.0, 6.6666]
.iter()
.map(|&f| Ieee32::from(f))
.collect();
assert_eq!(
V128Imm::from_iter(ieee32s).0,
[
/* 32.4 == */ 0x9a, 0x99, 0x01, 0x42, /* 0 == */ 0, 0, 0, 0,
/* 1 == */ 0, 0, 0x80, 0x3f, /* 6.6666 == */ 0xca, 0x54, 0xd5, 0x40,
]
)
}
#[test] #[test]
fn format_offset32() { fn format_offset32() {
assert_eq!(Offset32(0).to_string(), ""); assert_eq!(Offset32(0).to_string(), "");

View File

@@ -5,9 +5,8 @@ use crate::bitset::BitSet;
use crate::cursor::{Cursor, FuncCursor}; use crate::cursor::{Cursor, FuncCursor};
use crate::flowgraph::ControlFlowGraph; use crate::flowgraph::ControlFlowGraph;
use crate::ir::condcodes::{FloatCC, IntCC}; use crate::ir::condcodes::{FloatCC, IntCC};
use crate::ir::immediates::V128Imm;
use crate::ir::types::*; use crate::ir::types::*;
use crate::ir::{self, Function, Inst, InstBuilder}; use crate::ir::{self, ConstantData, Function, Inst, InstBuilder};
use crate::isa::constraints::*; use crate::isa::constraints::*;
use crate::isa::enc_tables::*; use crate::isa::enc_tables::*;
use crate::isa::encoding::base_size; use crate::isa::encoding::base_size;
@@ -1111,7 +1110,11 @@ fn convert_ineg(
{ {
let value_type = pos.func.dfg.value_type(arg); let value_type = pos.func.dfg.value_type(arg);
if value_type.is_vector() && value_type.lane_type().is_int() { if value_type.is_vector() && value_type.lane_type().is_int() {
let zero_immediate = pos.func.dfg.constants.insert(V128Imm::from(0).to_vec()); let zero_immediate = pos
.func
.dfg
.constants
.insert(ConstantData::from(vec![0; 16]));
let zero_value = pos.ins().vconst(value_type, zero_immediate); // this should be legalized to a PXOR let zero_value = pos.ins().vconst(value_type, zero_immediate); // this should be legalized to a PXOR
pos.func.dfg.replace(inst).isub(zero_value, arg); pos.func.dfg.replace(inst).isub(zero_value, arg);
} }

View File

@@ -5,7 +5,6 @@
use crate::entity::SecondaryMap; use crate::entity::SecondaryMap;
use crate::ir::entities::AnyEntity; use crate::ir::entities::AnyEntity;
use crate::ir::immediates::V128Imm;
use crate::ir::{ use crate::ir::{
DataFlowGraph, DisplayFunctionAnnotations, Ebb, Function, Inst, SigRef, Type, Value, ValueDef, DataFlowGraph, DisplayFunctionAnnotations, Ebb, Function, Inst, SigRef, Type, Value, ValueDef,
ValueLoc, ValueLoc,
@@ -515,8 +514,7 @@ pub fn write_operands(
let data = dfg.immediates.get(mask).expect( let data = dfg.immediates.get(mask).expect(
"Expected the shuffle mask to already be inserted into the immediates table", "Expected the shuffle mask to already be inserted into the immediates table",
); );
let v128 = V128Imm::from(&data[..]); write!(w, " {}, {}, {}", args[0], args[1], data)
write!(w, " {}, {}, {}", args[0], args[1], v128)
} }
IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),

View File

@@ -9,20 +9,19 @@ use crate::testfile::{Comment, Details, Feature, TestFile};
use cranelift_codegen::entity::EntityRef; use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_codegen::ir::entities::AnyEntity; use cranelift_codegen::ir::entities::AnyEntity;
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64, Imm64, Offset32, Uimm32, Uimm64, V128Imm}; use cranelift_codegen::ir::immediates::{Ieee32, Ieee64, Imm64, Offset32, Uimm32, Uimm64};
use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, VariableArgs}; use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, VariableArgs};
use cranelift_codegen::ir::types::INVALID; use cranelift_codegen::ir::types::INVALID;
use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{ use cranelift_codegen::ir::{
AbiParam, ArgumentExtension, ArgumentLoc, Ebb, ExtFuncData, ExternalName, FuncRef, Function, AbiParam, ArgumentExtension, ArgumentLoc, ConstantData, Ebb, ExtFuncData, ExternalName,
GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, JumpTable, JumpTableData, MemFlags, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, JumpTable,
Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, Table, TableData, Type, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind,
Value, ValueLoc, Table, TableData, Type, Value, ValueLoc,
}; };
use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa}; use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa};
use cranelift_codegen::packed_option::ReservedValue; use cranelift_codegen::packed_option::ReservedValue;
use cranelift_codegen::{settings, timing}; use cranelift_codegen::{settings, timing};
use std::iter::FromIterator;
use std::mem; use std::mem;
use std::str::FromStr; use std::str::FromStr;
use std::{u16, u32}; use std::{u16, u32};
@@ -595,16 +594,13 @@ impl<'a> Parser<'a> {
} }
} }
// Match and consume a Uimm128 immediate; due to size restrictions on InstructionData, Uimm128 // Match and consume a hexadeximal immediate
// is boxed in cranelift-codegen/meta/src/shared/immediates.rs fn match_hexadecimal_constant(&mut self, err_msg: &str) -> ParseResult<ConstantData> {
fn match_uimm128(&mut self, err_msg: &str) -> ParseResult<V128Imm> {
if let Some(Token::Integer(text)) = self.token() { if let Some(Token::Integer(text)) = self.token() {
self.consume(); self.consume();
// Lexer just gives us raw text that looks like hex code.
// Parse it as an Uimm128 to check for overflow and other issues.
text.parse().map_err(|e| { text.parse().map_err(|e| {
self.error(&format!( self.error(&format!(
"expected u128 hexadecimal immediate, failed to parse: {}", "expected hexadecimal immediate, failed to parse: {}",
e e
)) ))
}) })
@@ -614,15 +610,27 @@ impl<'a> Parser<'a> {
} }
// Match and consume either a hexadecimal Uimm128 immediate (e.g. 0x000102...) or its literal list form (e.g. [0 1 2...]) // Match and consume either a hexadecimal Uimm128 immediate (e.g. 0x000102...) or its literal list form (e.g. [0 1 2...])
fn match_uimm128_or_literals(&mut self, controlling_type: Type) -> ParseResult<V128Imm> { fn match_constant_data(&mut self, controlling_type: Type) -> ParseResult<ConstantData> {
if self.optional(Token::LBracket) { let expected_size = controlling_type.bytes() as usize;
let constant_data = if self.optional(Token::LBracket) {
// parse using a list of values, e.g. vconst.i32x4 [0 1 2 3] // parse using a list of values, e.g. vconst.i32x4 [0 1 2 3]
let uimm128 = self.parse_literals_to_uimm128(controlling_type)?; let uimm128 = self.parse_literals_to_uimm128(controlling_type)?;
self.match_token(Token::RBracket, "expected a terminating right bracket")?; self.match_token(Token::RBracket, "expected a terminating right bracket")?;
Ok(uimm128) uimm128
} else { } else {
// parse using a hexadecimal value // parse using a hexadecimal value, e.g. 0x000102...
self.match_uimm128("expected an immediate hexadecimal operand") let uimm128 =
self.match_hexadecimal_constant("expected an immediate hexadecimal operand")?;
uimm128.expand_to(expected_size)
};
if constant_data.len() == expected_size {
Ok(constant_data)
} else {
Err(self.error(&format!(
"expected parsed constant to have {} bytes",
expected_size
)))
} }
} }
@@ -851,29 +859,42 @@ impl<'a> Parser<'a> {
} }
/// Parse a list of literals (i.e. integers, floats, booleans); e.g. /// Parse a list of literals (i.e. integers, floats, booleans); e.g.
fn parse_literals_to_uimm128(&mut self, ty: Type) -> ParseResult<V128Imm> { fn parse_literals_to_uimm128(&mut self, ty: Type) -> ParseResult<ConstantData> {
macro_rules! consume { macro_rules! consume {
( $ty:ident, $match_fn:expr ) => {{ ( $ty:ident, $match_fn:expr ) => {{
assert!($ty.is_vector()); assert!($ty.is_vector());
let mut v = Vec::with_capacity($ty.lane_count() as usize); let mut data = ConstantData::default();
for _ in 0..$ty.lane_count() { for _ in 0..$ty.lane_count() {
v.push($match_fn?); data = data.append($match_fn);
} }
V128Imm::from_iter(v) data
}}; }};
} }
fn boolean_to_vec(value: bool, ty: Type) -> Vec<u8> {
let lane_size = ty.bytes() / u32::from(ty.lane_count());
if lane_size < 1 {
panic!("The boolean lane must have a byte size greater than zero.");
}
let mut buffer = vec![0; lane_size as usize];
buffer[0] = if value { 1 } else { 0 };
buffer
}
if !ty.is_vector() { if !ty.is_vector() {
err!(self.loc, "Expected a controlling vector type, not {}", ty) err!(self.loc, "Expected a controlling vector type, not {}", ty)
} else { } else {
let uimm128 = match ty.lane_type() { let uimm128 = match ty.lane_type() {
I8 => consume!(ty, self.match_uimm8("Expected an 8-bit unsigned integer")), I8 => consume!(ty, self.match_uimm8("Expected an 8-bit unsigned integer")?),
I16 => consume!(ty, self.match_imm16("Expected a 16-bit integer")), I16 => consume!(ty, self.match_imm16("Expected a 16-bit integer")?),
I32 => consume!(ty, self.match_imm32("Expected a 32-bit integer")), I32 => consume!(ty, self.match_imm32("Expected a 32-bit integer")?),
I64 => consume!(ty, self.match_imm64("Expected a 64-bit integer")), I64 => consume!(ty, self.match_imm64("Expected a 64-bit integer")?),
F32 => consume!(ty, self.match_ieee32("Expected a 32-bit float...")), F32 => consume!(ty, self.match_ieee32("Expected a 32-bit float...")?),
F64 => consume!(ty, self.match_ieee64("Expected a 64-bit float")), F64 => consume!(ty, self.match_ieee64("Expected a 64-bit float")?),
b if b.is_bool() => consume!(ty, self.match_bool("Expected a boolean")), b if b.is_bool() => consume!(
ty,
boolean_to_vec(self.match_bool("Expected a boolean")?, ty)
),
_ => return err!(self.loc, "Expected a type of: float, int, bool"), _ => return err!(self.loc, "Expected a type of: float, int, bool"),
}; };
Ok(uimm128) Ok(uimm128)
@@ -2447,8 +2468,8 @@ impl<'a> Parser<'a> {
) )
} }
Some(controlling_type) => { Some(controlling_type) => {
let uimm128 = self.match_uimm128_or_literals(controlling_type)?; let uimm128 = self.match_constant_data(controlling_type)?;
let constant_handle = ctx.function.dfg.constants.insert(uimm128.to_vec()); let constant_handle = ctx.function.dfg.constants.insert(uimm128);
InstructionData::UnaryConst { InstructionData::UnaryConst {
opcode, opcode,
constant_handle, constant_handle,
@@ -2460,8 +2481,8 @@ impl<'a> Parser<'a> {
self.match_token(Token::Comma, "expected ',' between operands")?; self.match_token(Token::Comma, "expected ',' between operands")?;
let b = self.match_value("expected SSA value second operand")?; let b = self.match_value("expected SSA value second operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?; self.match_token(Token::Comma, "expected ',' between operands")?;
let uimm128 = self.match_uimm128_or_literals(I8X16)?; let uimm128 = self.match_constant_data(I8X16)?;
let mask = ctx.function.dfg.immediates.push(uimm128.to_vec()); let mask = ctx.function.dfg.immediates.push(uimm128);
InstructionData::Shuffle { InstructionData::Shuffle {
opcode, opcode,
mask, mask,
@@ -3263,4 +3284,12 @@ mod tests {
cannot_parse_as_uimm128!("1 2 3", I32X4); cannot_parse_as_uimm128!("1 2 3", I32X4);
cannot_parse_as_uimm128!(" ", F32X4); cannot_parse_as_uimm128!(" ", F32X4);
} }
#[test]
fn parse_constant_from_booleans() {
let c = Parser::new("true false true false")
.parse_literals_to_uimm128(B32X4)
.unwrap();
assert_eq!(c.to_vec(), [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
}
} }

View File

@@ -33,7 +33,9 @@ use crate::wasm_unsupported;
use core::{i32, u32}; use core::{i32, u32};
use cranelift_codegen::ir::condcodes::{FloatCC, IntCC}; use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{self, InstBuilder, JumpTableData, MemFlags, Value, ValueLabel}; use cranelift_codegen::ir::{
self, ConstantData, InstBuilder, JumpTableData, MemFlags, Value, ValueLabel,
};
use cranelift_codegen::packed_option::ReservedValue; use cranelift_codegen::packed_option::ReservedValue;
use cranelift_frontend::{FunctionBuilder, Variable}; use cranelift_frontend::{FunctionBuilder, Variable};
use wasmparser::{MemoryImmediate, Operator}; use wasmparser::{MemoryImmediate, Operator};
@@ -1051,7 +1053,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
let (vector_a, vector_b) = state.pop2(); let (vector_a, vector_b) = state.pop2();
let a = optionally_bitcast_vector(vector_a, I8X16, builder); let a = optionally_bitcast_vector(vector_a, I8X16, builder);
let b = optionally_bitcast_vector(vector_b, I8X16, builder); let b = optionally_bitcast_vector(vector_b, I8X16, builder);
let mask = builder.func.dfg.immediates.push(lanes.to_vec()); let lanes = ConstantData::from(lanes.as_ref());
let mask = builder.func.dfg.immediates.push(lanes);
let shuffled = builder.ins().shuffle(a, b, mask); let shuffled = builder.ins().shuffle(a, b, mask);
state.push1(shuffled) state.push1(shuffled)
// At this point the original types of a and b are lost; users of this value (i.e. this // At this point the original types of a and b are lost; users of this value (i.e. this