diff --git a/cranelift/codegen/src/ir/constant.rs b/cranelift/codegen/src/ir/constant.rs index bd69e60c6f..f29c8fbada 100644 --- a/cranelift/codegen/src/ir/constant.rs +++ b/cranelift/codegen/src/ir/constant.rs @@ -8,19 +8,21 @@ //! - ensuring alignment of constants within the pool, //! - bucketing constants by size. +use crate::ir::immediates::{IntoBytes, V128Imm}; use crate::ir::Constant; use crate::HashMap; use alloc::collections::BTreeMap; use alloc::vec::Vec; use core::fmt; use core::slice::Iter; +use core::str::{from_utf8, FromStr}; use cranelift_entity::EntityRef; /// 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)] +#[derive(Clone, Hash, Eq, PartialEq, Debug, Default)] pub struct ConstantData(Vec); impl From> for ConstantData { @@ -35,16 +37,47 @@ impl From<&[u8]> for ConstantData { } } +impl From for ConstantData { + fn from(v: V128Imm) -> Self { + Self(v.to_vec()) + } +} + impl ConstantData { /// Return the number of bytes in the constant. pub fn len(&self) -> usize { self.0.len() } + /// Convert the data to a vector. + pub fn to_vec(self) -> Vec { + self.0 + } + /// Iterate over the constant's bytes. pub fn iter(&self) -> 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 { @@ -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 { + 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 = 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. pub type ConstantOffset = u32; @@ -303,4 +380,105 @@ mod tests { "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::().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::(); + 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::().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 { + from.parse::().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] + ); + } } diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs index 0ff6a263c4..b41f035822 100644 --- a/cranelift/codegen/src/ir/dfg.rs +++ b/cranelift/codegen/src/ir/dfg.rs @@ -5,7 +5,7 @@ use crate::ir; use crate::ir::builder::ReplaceBuilder; use crate::ir::extfunc::ExtFuncData; use crate::ir::instructions::{BranchInfo, CallInfo, InstructionData}; -use crate::ir::{types, ConstantPool, Immediate}; +use crate::ir::{types, ConstantData, ConstantPool, Immediate}; use crate::ir::{ Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueLabelAssignments, ValueList, ValueListPool, @@ -14,7 +14,6 @@ use crate::isa::TargetIsa; use crate::packed_option::ReservedValue; use crate::write::write_operands; use crate::HashMap; -use alloc::vec::Vec; use core::fmt; use core::iter; use core::mem; @@ -73,7 +72,7 @@ pub struct DataFlowGraph { pub constants: ConstantPool, /// Stores large immediates that otherwise will not fit on InstructionData - pub immediates: PrimaryMap>, + pub immediates: PrimaryMap, } impl DataFlowGraph { diff --git a/cranelift/codegen/src/ir/immediates.rs b/cranelift/codegen/src/ir/immediates.rs index 8ca70e5a44..8222f612a4 100644 --- a/cranelift/codegen/src/ir/immediates.rs +++ b/cranelift/codegen/src/ir/immediates.rs @@ -6,13 +6,13 @@ use alloc::vec::Vec; use core::fmt::{self, Display, Formatter}; -use core::iter::FromIterator; -use core::str::{from_utf8, FromStr}; +use core::str::FromStr; use core::{i32, u32}; /// 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. -trait IntoBytes { +pub trait IntoBytes { + /// Return the little-endian byte representation of the implementing type. fn into_bytes(self) -> Vec; } @@ -34,6 +34,12 @@ impl IntoBytes for i32 { } } +impl IntoBytes for Vec { + fn into_bytes(self) -> Vec { + self + } +} + /// 64-bit immediate signed integer operand. /// /// 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 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 { fn from(slice: &[u8]) -> Self { 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 { - 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 = 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>(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 for V128Imm { - fn from_iter>(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. /// /// This is used to encode an immediate offset for load/store instructions. All supported ISAs have @@ -1074,117 +966,6 @@ mod tests { parse_err::("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::("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 uimm128_equivalence() { - assert_eq!( - "0x01".parse::().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::().unwrap().0, - [0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ); - assert_eq!( - "0x00".parse::().unwrap().0, - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ); - assert_eq!( - "0x12345678".parse::().unwrap().0, - [0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ); - assert_eq!( - "0x1234_5678".parse::().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 = 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] fn format_offset32() { assert_eq!(Offset32(0).to_string(), ""); diff --git a/cranelift/codegen/src/isa/x86/enc_tables.rs b/cranelift/codegen/src/isa/x86/enc_tables.rs index 64c0499d40..4b01e298d1 100644 --- a/cranelift/codegen/src/isa/x86/enc_tables.rs +++ b/cranelift/codegen/src/isa/x86/enc_tables.rs @@ -5,9 +5,8 @@ use crate::bitset::BitSet; use crate::cursor::{Cursor, FuncCursor}; use crate::flowgraph::ControlFlowGraph; use crate::ir::condcodes::{FloatCC, IntCC}; -use crate::ir::immediates::V128Imm; 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::enc_tables::*; use crate::isa::encoding::base_size; @@ -1111,7 +1110,11 @@ fn convert_ineg( { let value_type = pos.func.dfg.value_type(arg); 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 pos.func.dfg.replace(inst).isub(zero_value, arg); } diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs index 910d1b988e..f2758e0c30 100644 --- a/cranelift/codegen/src/write.rs +++ b/cranelift/codegen/src/write.rs @@ -5,7 +5,6 @@ use crate::entity::SecondaryMap; use crate::ir::entities::AnyEntity; -use crate::ir::immediates::V128Imm; use crate::ir::{ DataFlowGraph, DisplayFunctionAnnotations, Ebb, Function, Inst, SigRef, Type, Value, ValueDef, ValueLoc, @@ -515,8 +514,7 @@ pub fn write_operands( let data = dfg.immediates.get(mask).expect( "Expected the shuffle mask to already be inserted into the immediates table", ); - let v128 = V128Imm::from(&data[..]); - write!(w, " {}, {}, {}", args[0], args[1], v128) + write!(w, " {}, {}, {}", args[0], args[1], data) } IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 82437d7c6c..b4ca9b7ccd 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -9,20 +9,19 @@ use crate::testfile::{Comment, Details, Feature, TestFile}; use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir; 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::types::INVALID; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{ - AbiParam, ArgumentExtension, ArgumentLoc, Ebb, ExtFuncData, ExternalName, FuncRef, Function, - GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, JumpTable, JumpTableData, MemFlags, - Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, Table, TableData, Type, - Value, ValueLoc, + AbiParam, ArgumentExtension, ArgumentLoc, ConstantData, Ebb, ExtFuncData, ExternalName, + FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, JumpTable, + JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, + Table, TableData, Type, Value, ValueLoc, }; use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa}; use cranelift_codegen::packed_option::ReservedValue; use cranelift_codegen::{settings, timing}; -use std::iter::FromIterator; use std::mem; use std::str::FromStr; 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 - // is boxed in cranelift-codegen/meta/src/shared/immediates.rs - fn match_uimm128(&mut self, err_msg: &str) -> ParseResult { + // Match and consume a hexadeximal immediate + fn match_hexadecimal_constant(&mut self, err_msg: &str) -> ParseResult { if let Some(Token::Integer(text)) = self.token() { 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| { self.error(&format!( - "expected u128 hexadecimal immediate, failed to parse: {}", + "expected hexadecimal immediate, failed to parse: {}", 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...]) - fn match_uimm128_or_literals(&mut self, controlling_type: Type) -> ParseResult { - if self.optional(Token::LBracket) { + fn match_constant_data(&mut self, controlling_type: Type) -> ParseResult { + 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] let uimm128 = self.parse_literals_to_uimm128(controlling_type)?; self.match_token(Token::RBracket, "expected a terminating right bracket")?; - Ok(uimm128) + uimm128 } else { - // parse using a hexadecimal value - self.match_uimm128("expected an immediate hexadecimal operand") + // parse using a hexadecimal value, e.g. 0x000102... + 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. - fn parse_literals_to_uimm128(&mut self, ty: Type) -> ParseResult { + fn parse_literals_to_uimm128(&mut self, ty: Type) -> ParseResult { macro_rules! consume { ( $ty:ident, $match_fn:expr ) => {{ 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() { - v.push($match_fn?); + data = data.append($match_fn); } - V128Imm::from_iter(v) + data }}; } + fn boolean_to_vec(value: bool, ty: Type) -> Vec { + 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() { err!(self.loc, "Expected a controlling vector type, not {}", ty) } else { let uimm128 = match ty.lane_type() { - I8 => consume!(ty, self.match_uimm8("Expected an 8-bit unsigned integer")), - I16 => consume!(ty, self.match_imm16("Expected a 16-bit integer")), - I32 => consume!(ty, self.match_imm32("Expected a 32-bit integer")), - I64 => consume!(ty, self.match_imm64("Expected a 64-bit integer")), - F32 => consume!(ty, self.match_ieee32("Expected a 32-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")), + I8 => consume!(ty, self.match_uimm8("Expected an 8-bit unsigned integer")?), + I16 => consume!(ty, self.match_imm16("Expected a 16-bit integer")?), + I32 => consume!(ty, self.match_imm32("Expected a 32-bit integer")?), + I64 => consume!(ty, self.match_imm64("Expected a 64-bit integer")?), + F32 => consume!(ty, self.match_ieee32("Expected a 32-bit float...")?), + F64 => consume!(ty, self.match_ieee64("Expected a 64-bit float")?), + 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"), }; Ok(uimm128) @@ -2447,8 +2468,8 @@ impl<'a> Parser<'a> { ) } Some(controlling_type) => { - let uimm128 = self.match_uimm128_or_literals(controlling_type)?; - let constant_handle = ctx.function.dfg.constants.insert(uimm128.to_vec()); + let uimm128 = self.match_constant_data(controlling_type)?; + let constant_handle = ctx.function.dfg.constants.insert(uimm128); InstructionData::UnaryConst { opcode, constant_handle, @@ -2460,8 +2481,8 @@ impl<'a> Parser<'a> { self.match_token(Token::Comma, "expected ',' between operands")?; let b = self.match_value("expected SSA value second operand")?; self.match_token(Token::Comma, "expected ',' between operands")?; - let uimm128 = self.match_uimm128_or_literals(I8X16)?; - let mask = ctx.function.dfg.immediates.push(uimm128.to_vec()); + let uimm128 = self.match_constant_data(I8X16)?; + let mask = ctx.function.dfg.immediates.push(uimm128); InstructionData::Shuffle { opcode, mask, @@ -3263,4 +3284,12 @@ mod tests { cannot_parse_as_uimm128!("1 2 3", I32X4); 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]) + } } diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index 6dd7deb1ff..9f2aee1f91 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -33,7 +33,9 @@ use crate::wasm_unsupported; use core::{i32, u32}; use cranelift_codegen::ir::condcodes::{FloatCC, IntCC}; 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_frontend::{FunctionBuilder, Variable}; use wasmparser::{MemoryImmediate, Operator}; @@ -1051,7 +1053,8 @@ pub fn translate_operator( let (vector_a, vector_b) = state.pop2(); let a = optionally_bitcast_vector(vector_a, 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); state.push1(shuffled) // At this point the original types of a and b are lost; users of this value (i.e. this