diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index f5a10bd151..7ecf6473b4 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -3,6 +3,7 @@ use crate::error::{Location, ParseError, ParseResult}; use crate::isaspec; use crate::lexer::{LexError, Lexer, LocatedError, LocatedToken, Token}; +use crate::run_command::{Comparison, DataValue, Invocation, RunCommand}; use crate::sourcemap::SourceMap; use crate::testcommand::TestCommand; use crate::testfile::{Comment, Details, Feature, TestFile}; @@ -2392,6 +2393,160 @@ impl<'a> Parser<'a> { Ok(args) } + /// Parse a CLIF run command. + /// + /// run-command ::= "run" [":" invocation comparison expected] + /// \ "print" [":" invocation] + fn parse_run_command(&mut self, sig: &Signature) -> ParseResult { + // skip semicolon + match self.token() { + Some(Token::Identifier("run")) => { + self.consume(); + if self.optional(Token::Colon) { + let invocation = self.parse_run_invocation(sig)?; + let comparison = self.parse_run_comparison()?; + let expected = self.parse_run_returns(sig)?; + Ok(RunCommand::Run(invocation, comparison, expected)) + } else if sig.params.is_empty() + && sig.returns.len() == 1 + && sig.returns[0].value_type.is_bool() + { + // To match the existing run behavior that does not require an explicit + // invocation, we create an invocation from a function like `() -> b*` and + // compare it to `true`. + let invocation = Invocation::new("default", vec![]); + let expected = vec![DataValue::B(true)]; + let comparison = Comparison::Equals; + Ok(RunCommand::Run(invocation, comparison, expected)) + } else { + Err(self.error("unable to parse the run command")) + } + } + Some(Token::Identifier("print")) => { + self.consume(); + if self.optional(Token::Colon) { + Ok(RunCommand::Print(self.parse_run_invocation(sig)?)) + } else if sig.params.is_empty() { + // To allow printing of functions like `() -> *`, we create a no-arg invocation. + let invocation = Invocation::new("default", vec![]); + Ok(RunCommand::Print(invocation)) + } else { + Err(self.error("unable to parse the print command")) + } + } + _ => Err(self.error("expected a 'run:' or 'print:' command")), + } + } + + /// Parse the invocation of a CLIF function. + /// + /// This is different from parsing a CLIF `call`; it is used in parsing run commands like + /// `run: %fn(42, 4.2) == false`. + /// + /// invocation ::= name "(" [data-value-list] ")" + fn parse_run_invocation(&mut self, sig: &Signature) -> ParseResult { + if let Some(Token::Name(name)) = self.token() { + self.consume(); + self.match_token( + Token::LPar, + "expected invocation parentheses, e.g. %fn(...)", + )?; + + let args = self.parse_data_value_list( + &sig.params.iter().map(|a| a.value_type).collect::>(), + )?; + + self.match_token( + Token::RPar, + "expected invocation parentheses, e.g. %fn(...)", + )?; + Ok(Invocation::new(name, args)) + } else { + Err(self.error("expected a function name, e.g. %my_fn")) + } + } + + /// Parse a comparison operator for run commands. + /// + /// comparison ::= "==" | "!=" + fn parse_run_comparison(&mut self) -> ParseResult { + if self.optional(Token::Equal) { + self.match_token(Token::Equal, "expected another =")?; + Ok(Comparison::Equals) + } else if self.optional(Token::Not) { + self.match_token(Token::Equal, "expected a =")?; + Ok(Comparison::NotEquals) + } else { + Err(self.error("unable to parse a valid comparison operator")) + } + } + + /// Parse the expected return values of a run invocation. + /// + /// expected ::= "[" "]" + /// | data-value + /// | "[" data-value-list "]" + fn parse_run_returns(&mut self, sig: &Signature) -> ParseResult> { + if sig.returns.len() != 1 { + self.match_token(Token::LBracket, "expected a left bracket [")?; + } + + let returns = self + .parse_data_value_list(&sig.returns.iter().map(|a| a.value_type).collect::>())?; + + if sig.returns.len() != 1 { + self.match_token(Token::RBracket, "expected a right bracket ]")?; + } + Ok(returns) + } + + /// Parse a comma-separated list of data values. + /// + /// data-value-list ::= [data-value {"," data-value-list}] + fn parse_data_value_list(&mut self, types: &[Type]) -> ParseResult> { + let mut values = vec![]; + for ty in types.iter().take(1) { + values.push(self.parse_data_value(*ty)?); + } + for ty in types.iter().skip(1) { + self.match_token( + Token::Comma, + "expected a comma between invocation arguments", + )?; + values.push(self.parse_data_value(*ty)?); + } + Ok(values) + } + + /// Parse a data value; e.g. `42`, `4.2`, `true`. + /// + /// data-value-list ::= [data-value {"," data-value-list}] + fn parse_data_value(&mut self, ty: Type) -> ParseResult { + let dv = match ty { + I8 => DataValue::from(self.match_imm8("expected a i8")?), + I16 => DataValue::from(self.match_imm16("expected an i16")?), + I32 => DataValue::from(self.match_imm32("expected an i32")?), + I64 => DataValue::from(Into::::into(self.match_imm64("expected an i64")?)), + F32 => DataValue::from(f32::from_bits(self.match_ieee32("expected an f32")?.bits())), + F64 => DataValue::from(f64::from_bits(self.match_ieee64("expected an f64")?.bits())), + _ if ty.is_vector() => { + let as_vec = self.match_constant_data(ty)?.into_vec(); + if as_vec.len() == 16 { + let mut as_array = [0; 16]; + as_array.copy_from_slice(&as_vec[..16]); + DataValue::from(as_array) + } else { + return Err(self.error("only 128-bit vectors are currently supported")); + } + } + _ if ty.is_bool() && !ty.is_vector() => { + DataValue::from(self.match_bool("expected a boolean")?) + } + _ => return Err(self.error(&format!("don't know how to parse data values of: {}", ty))), + }; + Ok(dv) + } + // Parse the operands following the instruction opcode. // This depends on the format of the opcode. fn parse_inst_operands( @@ -3529,4 +3684,76 @@ mod tests { [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] ) } + + #[test] + fn parse_run_commands() { + // Helper for creating signatures. + fn sig(ins: &[Type], outs: &[Type]) -> Signature { + let mut sig = Signature::new(CallConv::Fast); + for i in ins { + sig.params.push(AbiParam::new(*i)); + } + for o in outs { + sig.returns.push(AbiParam::new(*o)); + } + sig + } + + // Helper for parsing run commands. + fn parse(text: &str, sig: &Signature) -> ParseResult { + Parser::new(text).parse_run_command(sig) + } + + // Check that we can parse and display the same set of run commands. + fn assert_roundtrip(text: &str, sig: &Signature) { + assert_eq!(parse(text, sig).unwrap().to_string(), text); + } + assert_roundtrip("run: %fn0() == 42", &sig(&[], &[I32])); + assert_roundtrip( + "run: %fn0(8, 16, 32, 64) == true", + &sig(&[I8, I16, I32, I64], &[B8]), + ); + assert_roundtrip( + "run: %my_func(true) == 0x0f0e0d0c0b0a09080706050403020100", + &sig(&[B32], &[I8X16]), + ); + + // Verify that default invocations are created when not specified. + assert_eq!( + parse("run", &sig(&[], &[B32])).unwrap().to_string(), + "run: %default() == true" + ); + assert_eq!( + parse("print", &sig(&[], &[F32X4, I16X8])) + .unwrap() + .to_string(), + "print: %default()" + ); + + // Demonstrate some unparseable cases. + assert!(parse("print", &sig(&[I32], &[B32])).is_err()); + assert!(parse("run", &sig(&[], &[I32])).is_err()); + assert!(parse("print:", &sig(&[], &[])).is_err()); + assert!(parse("run: ", &sig(&[], &[])).is_err()); + } + + #[test] + fn parse_data_values() { + fn parse(text: &str, ty: Type) -> DataValue { + Parser::new(text).parse_data_value(ty).unwrap() + } + + assert_eq!(parse("8", I8).to_string(), "8"); + assert_eq!(parse("16", I16).to_string(), "16"); + assert_eq!(parse("32", I32).to_string(), "32"); + assert_eq!(parse("64", I64).to_string(), "64"); + assert_eq!(parse("0x32.32", F32).to_string(), "0x1.919000p5"); + assert_eq!(parse("0x64.64", F64).to_string(), "0x1.9190000000000p6"); + assert_eq!(parse("true", B1).to_string(), "true"); + assert_eq!(parse("false", B64).to_string(), "false"); + assert_eq!( + parse("[0 1 2 3]", I32X4).to_string(), + "0x03000000020000000100000000" + ); + } }