diff --git a/cranelift/filetests/filetests/verifier/constant.clif b/cranelift/filetests/filetests/verifier/constant.clif index a93abb82d7..ac6aff2047 100644 --- a/cranelift/filetests/filetests/verifier/constant.clif +++ b/cranelift/filetests/filetests/verifier/constant.clif @@ -2,7 +2,7 @@ test verifier set enable_simd function %incorrect_constant_size() { -const13 = [1 2 3 4 5] ; this constant has 5 bytes +const13 = 0x0102030405 ; this constant has 5 bytes block0: v0 = vconst.i32x4 const13 ; error: The instruction expects const13 to have a size of 16 bytes but it has 5 return diff --git a/cranelift/fuzzgen/src/cranelift_arbitrary.rs b/cranelift/fuzzgen/src/cranelift_arbitrary.rs index ca59135aef..ebafb55d89 100644 --- a/cranelift/fuzzgen/src/cranelift_arbitrary.rs +++ b/cranelift/fuzzgen/src/cranelift_arbitrary.rs @@ -7,27 +7,39 @@ use cranelift::codegen::isa::CallConv; use arbitrary::Unstructured; use cranelift::prelude::{Ieee32, Ieee64}; +use target_lexicon::Architecture; /// A trait for generating random Cranelift datastructures. pub trait CraneliftArbitrary { - fn _type(&mut self) -> Result; + fn _type(&mut self, architecture: Architecture) -> Result; fn callconv(&mut self) -> Result; - fn abi_param(&mut self) -> Result; - fn signature(&mut self, max_params: usize, max_rets: usize) -> Result; + fn abi_param(&mut self, architecture: Architecture) -> Result; + fn signature( + &mut self, + architecture: Architecture, + max_params: usize, + max_rets: usize, + ) -> Result; fn datavalue(&mut self, ty: Type) -> Result; } impl<'a> CraneliftArbitrary for &mut Unstructured<'a> { - fn _type(&mut self) -> Result { + fn _type(&mut self, architecture: Architecture) -> Result { // TODO: It would be nice if we could get these directly from cranelift - let scalars = [ - I8, I16, I32, I64, I128, F32, F64, - // R32, R64, - ]; - // TODO: vector types + // TODO: RISCV does not support SIMD yet + let supports_simd = !matches!(architecture, Architecture::Riscv64(_)); + let choices = if supports_simd { + &[ + I8, I16, I32, I64, I128, // Scalar Integers + F32, F64, // Scalar Floats + I8X16, I16X8, I32X4, I64X2, // SIMD Integers + F32X4, F64X2, // SIMD Floats + ][..] + } else { + &[I8, I16, I32, I64, I128, F32, F64][..] + }; - let ty = self.choose(&scalars[..])?; - Ok(*ty) + Ok(*self.choose(choices)?) } fn callconv(&mut self) -> Result { @@ -35,8 +47,8 @@ impl<'a> CraneliftArbitrary for &mut Unstructured<'a> { Ok(CallConv::SystemV) } - fn abi_param(&mut self) -> Result { - let value_type = self._type()?; + fn abi_param(&mut self, architecture: Architecture) -> Result { + let value_type = self._type(architecture)?; // TODO: There are more argument purposes to be explored... let purpose = ArgumentPurpose::Normal; let extension = if value_type.is_int() { @@ -56,16 +68,21 @@ impl<'a> CraneliftArbitrary for &mut Unstructured<'a> { }) } - fn signature(&mut self, max_params: usize, max_rets: usize) -> Result { + fn signature( + &mut self, + architecture: Architecture, + max_params: usize, + max_rets: usize, + ) -> Result { let callconv = self.callconv()?; let mut sig = Signature::new(callconv); for _ in 0..max_params { - sig.params.push(self.abi_param()?); + sig.params.push(self.abi_param(architecture)?); } for _ in 0..max_rets { - sig.returns.push(self.abi_param()?); + sig.returns.push(self.abi_param(architecture)?); } Ok(sig) @@ -88,6 +105,9 @@ impl<'a> CraneliftArbitrary for &mut Unstructured<'a> { // such as Signaling NaN's / NaN's with payload, so generate floats from integers. F32 => DataValue::F32(Ieee32::with_bits(self.arbitrary::()?)), F64 => DataValue::F64(Ieee64::with_bits(self.arbitrary::()?)), + ty if ty.is_vector() && ty.bits() == 128 => { + DataValue::V128(self.arbitrary::<[u8; 16]>()?) + } _ => unimplemented!(), }) } diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs index 37c504d215..00c4a92127 100644 --- a/cranelift/fuzzgen/src/function_generator.rs +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -565,6 +565,12 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) - } Architecture::Riscv64(_) => { + // RISC-V Does not support SIMD at all + let is_simd = args.iter().chain(rets).any(|t| t.is_vector()); + if is_simd { + return false; + } + exceptions!( // TODO (Opcode::IaddCout), @@ -737,6 +743,10 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[ (Opcode::Iabs, &[I32], &[I32], insert_opcode), (Opcode::Iabs, &[I64], &[I64], insert_opcode), (Opcode::Iabs, &[I128], &[I128], insert_opcode), + (Opcode::Iabs, &[I8X16, I8X16], &[I8X16], insert_opcode), + (Opcode::Iabs, &[I16X8, I16X8], &[I16X8], insert_opcode), + (Opcode::Iabs, &[I32X4, I32X4], &[I32X4], insert_opcode), + (Opcode::Iabs, &[I64X2, I64X2], &[I64X2], insert_opcode), // Smin (Opcode::Smin, &[I8, I8], &[I8], insert_opcode), (Opcode::Smin, &[I16, I16], &[I16], insert_opcode), @@ -1552,6 +1562,11 @@ where } DataValue::F32(f) => builder.ins().f32const(f), DataValue::F64(f) => builder.ins().f64const(f), + DataValue::V128(bytes) => { + let data = bytes.to_vec().into(); + let handle = builder.func.dfg.constants.insert(data); + builder.ins().vconst(ty, handle) + } _ => unimplemented!(), }) } @@ -1922,7 +1937,7 @@ where let mut params = Vec::with_capacity(param_count); for _ in 0..param_count { - params.push(self.u._type()?); + params.push(self.u._type(self.target_triple.architecture)?); } Ok(params) } @@ -1942,7 +1957,7 @@ where // Create a pool of vars that are going to be used in this function for _ in 0..self.param(&self.config.vars_per_function)? { - let ty = self.u._type()?; + let ty = self.u._type(self.target_triple.architecture)?; let value = self.generate_const(builder, ty)?; vars.push((ty, value)); } diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index ee0c36423a..d036650040 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -256,7 +256,9 @@ where fn generate_func(&mut self, target_triple: Triple) -> Result { let max_params = self.u.int_in_range(self.config.signature_params.clone())?; let max_rets = self.u.int_in_range(self.config.signature_rets.clone())?; - let sig = self.u.signature(max_params, max_rets)?; + let sig = self + .u + .signature(target_triple.architecture, max_params, max_rets)?; // Function name must be in a different namespace than TESTFILE_NAMESPACE (0) let fname = UserFuncName::user(1, 0); @@ -266,7 +268,9 @@ where .map(|i| { let max_params = self.u.int_in_range(self.config.signature_params.clone())?; let max_rets = self.u.int_in_range(self.config.signature_rets.clone())?; - let sig = self.u.signature(max_params, max_rets)?; + let sig = self + .u + .signature(target_triple.architecture, max_params, max_rets)?; let name = UserExternalName { namespace: 2, index: i as u32, diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index fca5fa499a..1e2be48243 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -729,16 +729,6 @@ impl<'a> Parser<'a> { } } - // Match and consume a sequence of immediate bytes (uimm8); e.g. [0x42 0x99 0x32] - fn match_constant_data(&mut self) -> ParseResult { - self.match_token(Token::LBracket, "expected an opening left bracket")?; - let mut data = ConstantData::default(); - while !self.optional(Token::RBracket) { - data = data.append(self.match_uimm8("expected a sequence of bytes (uimm8)")?); - } - Ok(data) - } - // Match and consume either a hexadecimal Uimm128 immediate (e.g. 0x000102...) or its literal // list form (e.g. [0 1 2...]). For convenience, since uimm128 values are stored in the // `ConstantPool`, this returns `ConstantData`. @@ -1840,7 +1830,7 @@ impl<'a> Parser<'a> { let ty = self.match_type("expected type of constant")?; self.match_uimm128(ty) } else { - self.match_constant_data() + self.match_hexadecimal_constant("expected an immediate hexadecimal operand") }?; // Collect any trailing comments. @@ -3440,14 +3430,18 @@ mod tests { #[test] fn parse_unbounded_constants() { - // Unlike match_uimm128, match_constant_data can parse byte sequences of any size: + // Unlike match_uimm128, match_hexadecimal_constant can parse byte sequences of any size: assert_eq!( - Parser::new("[0 1]").match_constant_data().unwrap(), + Parser::new("0x0100") + .match_hexadecimal_constant("err message") + .unwrap(), vec![0, 1].into() ); - // Only parse byte literals: - assert!(Parser::new("[256]").match_constant_data().is_err()); + // Only parse hexadecimal constants: + assert!(Parser::new("228") + .match_hexadecimal_constant("err message") + .is_err()); } #[test]