From a5c4c5f237181951d65b8aff70afd401d1a17b65 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 7 Nov 2024 13:27:45 -0300 Subject: [PATCH 01/65] WIP --- compiler/noirc_evaluator/src/ssa.rs | 1 + compiler/noirc_evaluator/src/ssa/parser.rs | 25 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 compiler/noirc_evaluator/src/ssa/parser.rs diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 0c4e42f09ef..af3b7a0d675 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -45,6 +45,7 @@ mod checks; pub(super) mod function_builder; pub mod ir; mod opt; +mod parser; pub mod ssa_gen; pub struct SsaEvaluatorOptions { diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs new file mode 100644 index 00000000000..e2f47d7e3d2 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -0,0 +1,25 @@ +use super::Ssa; + +impl Ssa { + fn from_str(_str: &str) -> Result { + Err(()) + } +} + +#[cfg(test)] +mod tests { + use crate::ssa::Ssa; + + #[test] + fn test_ssa_from_str() { + let src = " + acir(inline) fn main f0 { + b0(): + return + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + println!("{}", ssa); + } +} From d610616ca2653a6a9ed80894e1cdc0c56e94a0dc Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 7 Nov 2024 16:13:34 -0300 Subject: [PATCH 02/65] SSA printer: don't leave extra space after return without values --- compiler/noirc_evaluator/src/ssa/ir/printer.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 2b564c14aa7..5816af8f426 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -120,7 +120,11 @@ pub(crate) fn display_terminator( ) } Some(TerminatorInstruction::Return { return_values, .. }) => { - writeln!(f, " return {}", value_list(function, return_values)) + if return_values.is_empty() { + writeln!(f, " return") + } else { + writeln!(f, " return {}", value_list(function, return_values)) + } } None => writeln!(f, " (no terminator instruction)"), } From 0876084f57eb19a4a2583f64a5e7a0d91c358e54 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 7 Nov 2024 16:13:54 -0300 Subject: [PATCH 03/65] SSA parser: parse an empty function --- compiler/noirc_evaluator/src/ssa/parser.rs | 244 +++++++++++++++++- .../noirc_evaluator/src/ssa/parser/ast.rs | 53 ++++ .../noirc_evaluator/src/ssa/parser/lexer.rs | 150 +++++++++++ .../noirc_evaluator/src/ssa/parser/tests.rs | 19 ++ .../noirc_evaluator/src/ssa/parser/token.rs | 77 ++++++ 5 files changed, 529 insertions(+), 14 deletions(-) create mode 100644 compiler/noirc_evaluator/src/ssa/parser/ast.rs create mode 100644 compiler/noirc_evaluator/src/ssa/parser/lexer.rs create mode 100644 compiler/noirc_evaluator/src/ssa/parser/tests.rs create mode 100644 compiler/noirc_evaluator/src/ssa/parser/token.rs diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index e2f47d7e3d2..f7271cdb223 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -1,25 +1,241 @@ use super::Ssa; +use ast::{ParsedBlock, ParsedFunction, ParsedSsa}; +use lexer::{Lexer, LexerError}; +use noirc_errors::Span; +use noirc_frontend::monomorphization::ast::InlineType; +use token::{Keyword, SpannedToken, Token}; + +use crate::ssa::{ir::function::RuntimeType, parser::ast::ParsedTerminator}; + +mod ast; +mod lexer; +mod tests; +mod token; + impl Ssa { - fn from_str(_str: &str) -> Result { - Err(()) + fn from_str(str: &str) -> Result { + let mut parser = Parser::new(str).map_err(SsaError::ParserError)?; + let parsed_ssa = parser.parse_ssa().map_err(SsaError::ParserError)?; + parsed_ssa.into_ssa() } } -#[cfg(test)] -mod tests { - use crate::ssa::Ssa; +#[derive(Debug)] +pub(crate) enum SsaError { + ParserError(ParserError), +} + +type ParseResult = Result; + +pub(crate) struct Parser<'a> { + lexer: Lexer<'a>, + token: SpannedToken, +} + +impl<'a> Parser<'a> { + pub(crate) fn new(source: &'a str) -> ParseResult { + let lexer = Lexer::new(source); + let mut parser = Self { lexer, token: eof_spanned_token() }; + parser.token = parser.read_token_internal()?; + Ok(parser) + } + + pub(crate) fn parse_ssa(&mut self) -> ParseResult { + let mut functions = Vec::new(); + while !self.at(Token::Eof) { + let function = self.parse_function()?; + functions.push(function); + } + Ok(ParsedSsa { functions }) + } + + fn parse_function(&mut self) -> ParseResult { + let runtime_type = self.parse_runtime_type()?; + self.eat_or_error(Token::Keyword(Keyword::Fn))?; + + let external_name = self.eat_ident_or_error()?; + let internal_name = self.eat_ident_or_error()?; + + self.eat_or_error(Token::LeftBrace)?; + + let blocks = self.parse_blocks()?; - #[test] - fn test_ssa_from_str() { - let src = " - acir(inline) fn main f0 { - b0(): - return + self.eat_or_error(Token::RightBrace)?; + + Ok(ParsedFunction { runtime_type, external_name, internal_name, blocks }) + } + + fn parse_runtime_type(&mut self) -> ParseResult { + let acir = if self.eat_keyword(Keyword::Acir)? { + true + } else if self.eat_keyword(Keyword::Brillig)? { + false + } else { + return self.expected_one_of_tokens(&[ + Token::Keyword(Keyword::Acir), + Token::Keyword(Keyword::Brillig), + ]); + }; + + self.eat_or_error(Token::LeftParen)?; + let inline_type = self.parse_inline_type()?; + self.eat_or_error(Token::RightParen)?; + + if acir { + Ok(RuntimeType::Acir(inline_type)) + } else { + Ok(RuntimeType::Brillig(inline_type)) + } + } + + fn parse_inline_type(&mut self) -> ParseResult { + if self.eat_keyword(Keyword::Inline)? { + Ok(InlineType::Inline) + } else if self.eat_keyword(Keyword::InlineAlways)? { + Ok(InlineType::InlineAlways) + } else if self.eat_keyword(Keyword::Fold)? { + Ok(InlineType::Fold) + } else if self.eat_keyword(Keyword::NoPredicates)? { + Ok(InlineType::NoPredicates) + } else { + self.expected_one_of_tokens(&[ + Token::Keyword(Keyword::Inline), + Token::Keyword(Keyword::InlineAlways), + Token::Keyword(Keyword::Fold), + Token::Keyword(Keyword::NoPredicates), + ]) + } + } + + fn parse_blocks(&mut self) -> ParseResult> { + let mut blocks = Vec::new(); + while !self.at(Token::RightBrace) { + let block = self.parse_block()?; + blocks.push(block); + } + Ok(blocks) + } + + fn parse_block(&mut self) -> ParseResult { + let name = self.eat_ident_or_error()?; + self.eat_or_error(Token::LeftParen)?; + self.eat_or_error(Token::RightParen)?; + self.eat_or_error(Token::Colon)?; + + let instructions = Vec::new(); + let terminator = self.parse_terminator()?; + Ok(ParsedBlock { name, instructions, terminator }) + } + + fn parse_terminator(&mut self) -> ParseResult { + if self.eat_keyword(Keyword::Return)? { + Ok(ParsedTerminator::Return) + } else { + self.expected_instruction_or_terminator() + } + } + + fn eat_keyword(&mut self, keyword: Keyword) -> ParseResult { + if let Token::Keyword(kw) = self.token.token() { + if *kw == keyword { + self.bump()?; + Ok(true) + } else { + Ok(false) } - "; + } else { + Ok(false) + } + } + + fn eat_ident(&mut self) -> ParseResult> { + if !matches!(self.token.token(), Token::Ident(..)) { + return Ok(None); + } + + let token = self.bump()?; + match token.into_token() { + Token::Ident(ident) => Ok(Some(ident)), + _ => unreachable!(), + } + } + + fn eat_ident_or_error(&mut self) -> ParseResult { + if let Some(ident) = self.eat_ident()? { + Ok(ident) + } else { + self.expected_identifier() + } + } + + fn eat(&mut self, token: Token) -> ParseResult { + if self.token.token() == &token { + self.bump()?; + Ok(true) + } else { + Ok(false) + } + } + + fn eat_or_error(&mut self, token: Token) -> ParseResult<()> { + if self.eat(token.clone())? { + Ok(()) + } else { + self.expected_token(token) + } + } + + fn at(&self, token: Token) -> bool { + self.token.token() == &token + } + + fn at_keyword(&self, keyword: Keyword) -> bool { + self.at(Token::Keyword(keyword)) + } - let ssa = Ssa::from_str(src).unwrap(); - println!("{}", ssa); + fn bump(&mut self) -> ParseResult { + let token = self.read_token_internal()?; + Ok(std::mem::replace(&mut self.token, token)) } + + fn read_token_internal(&mut self) -> ParseResult { + self.lexer.next_token().map_err(ParserError::LexerError) + } + + fn expected_instruction_or_terminator(&mut self) -> ParseResult { + Err(ParserError::ExpectedInstructionOrTerminator( + self.token.token().clone(), + self.token.to_span(), + )) + } + + fn expected_identifier(&mut self) -> ParseResult { + Err(ParserError::ExpectedIdentifier(self.token.token().clone(), self.token.to_span())) + } + + fn expected_token(&mut self, token: Token) -> ParseResult { + Err(ParserError::ExpectedToken(token, self.token.token().clone(), self.token.to_span())) + } + + fn expected_one_of_tokens(&mut self, tokens: &[Token]) -> ParseResult { + Err(ParserError::ExpectedOneOfTokens( + tokens.to_vec(), + self.token.token().clone(), + self.token.to_span(), + )) + } +} + +#[derive(Debug)] +pub(crate) enum ParserError { + LexerError(LexerError), + ExpectedToken(Token, Token, Span), + ExpectedOneOfTokens(Vec, Token, Span), + ExpectedIdentifier(Token, Span), + ExpectedInstructionOrTerminator(Token, Span), +} + +fn eof_spanned_token() -> SpannedToken { + SpannedToken::new(Token::Eof, Default::default()) } diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs new file mode 100644 index 00000000000..0df8eeb6ff0 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -0,0 +1,53 @@ +use crate::ssa::{ + function_builder::FunctionBuilder, + ir::function::{FunctionId, RuntimeType}, +}; + +use super::{Ssa, SsaError}; + +#[derive(Debug)] +pub(crate) struct ParsedSsa { + pub(crate) functions: Vec, +} + +impl ParsedSsa { + pub(crate) fn into_ssa(mut self) -> Result { + let mut main_function = self.functions.remove(0); + let main_id = FunctionId::new(0); + + let mut builder = FunctionBuilder::new(main_function.external_name, main_id); + builder.set_runtime(main_function.runtime_type); + + let entry_block = main_function.blocks.remove(0); + match entry_block.terminator { + ParsedTerminator::Return => { + builder.terminate_with_return(vec![]); + } + } + + Ok(builder.finish()) + } +} + +#[derive(Debug)] +pub(crate) struct ParsedFunction { + pub(crate) runtime_type: RuntimeType, + pub(crate) external_name: String, + pub(crate) internal_name: String, + pub(crate) blocks: Vec, +} + +#[derive(Debug)] +pub(crate) struct ParsedBlock { + pub(crate) name: String, + pub(crate) instructions: Vec, + pub(crate) terminator: ParsedTerminator, +} + +#[derive(Debug)] +pub(crate) enum ParsedInstruction {} + +#[derive(Debug)] +pub(crate) enum ParsedTerminator { + Return, +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs new file mode 100644 index 00000000000..6bcb18aa321 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -0,0 +1,150 @@ +use std::str::CharIndices; + +use noirc_errors::{Position, Span}; + +use super::token::{Keyword, SpannedToken, Token}; + +pub(crate) struct Lexer<'a> { + chars: CharIndices<'a>, + position: Position, + done: bool, +} + +impl<'a> Lexer<'a> { + pub(crate) fn new(source: &'a str) -> Self { + Lexer { chars: source.char_indices(), position: 0, done: false } + } + + pub(crate) fn next_token(&mut self) -> SpannedTokenResult { + match self.next_char() { + Some(char) if char.is_ascii_whitespace() => { + while let Some(char) = self.peek_char() { + if char.is_ascii_whitespace() { + self.next_char(); + } else { + break; + } + } + self.next_token() + } + Some(':') => self.single_char_token(Token::Colon), + Some('(') => self.single_char_token(Token::LeftParen), + Some(')') => self.single_char_token(Token::RightParen), + Some('{') => self.single_char_token(Token::LeftBrace), + Some('}') => self.single_char_token(Token::RightBrace), + Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch), + Some(char) => Err(LexerError::UnexpectedCharacter { + char, + span: Span::single_char(self.position), + }), + None => { + self.done = true; + Ok(Token::Eof.into_single_span(self.position)) + } + } + } + + fn eat_alpha_numeric(&mut self, initial_char: char) -> SpannedTokenResult { + match initial_char { + 'A'..='Z' | 'a'..='z' | '_' => Ok(self.eat_word(initial_char)?), + // '0'..='9' => self.eat_digit(initial_char), + _ => Err(LexerError::UnexpectedCharacter { + char: initial_char, + span: Span::single_char(self.position), + }), + } + } + + fn eat_word(&mut self, initial_char: char) -> SpannedTokenResult { + let (start, word, end) = self.lex_word(initial_char); + self.lookup_word_token(word, start, end) + } + + fn lex_word(&mut self, initial_char: char) -> (Position, String, Position) { + let start = self.position; + let word = self.eat_while(Some(initial_char), |ch| { + ch.is_ascii_alphabetic() || ch.is_numeric() || ch == '_' + }); + (start, word, self.position) + } + + fn lookup_word_token( + &self, + word: String, + start: Position, + end: Position, + ) -> SpannedTokenResult { + // Check if word either an identifier or a keyword + if let Some(keyword_token) = Keyword::lookup_keyword(&word) { + return Ok(keyword_token.into_span(start, end)); + } + + // Check if word an int type + // if no error occurred, then it is either a valid integer type or it is not an int type + // let parsed_token = IntType::lookup_int_type(&word)?; + + // // Check if it is an int type + // if let Some(int_type_token) = parsed_token { + // return Ok(int_type_token.into_span(start, end)); + // } + + // Else it is just an identifier + let ident_token = Token::Ident(word); + Ok(ident_token.into_span(start, end)) + } + + fn eat_while bool>( + &mut self, + initial_char: Option, + predicate: F, + ) -> String { + // This function is only called when we want to continue consuming a character of the same type. + // For example, we see a digit and we want to consume the whole integer + // Therefore, the current character which triggered this function will need to be appended + let mut word = String::new(); + if let Some(init_char) = initial_char { + word.push(init_char); + } + + // Keep checking that we are not at the EOF + while let Some(peek_char) = self.peek_char() { + // Then check for the predicate, if predicate matches append char and increment the cursor + // If not, return word. The next character will be analyzed on the next iteration of next_token, + // Which will increment the cursor + if !predicate(peek_char) { + return word; + } + word.push(peek_char); + + // If we arrive at this point, then the char has been added to the word and we should increment the cursor + self.next_char(); + } + + word + } + + fn single_char_token(&self, token: Token) -> SpannedTokenResult { + Ok(token.into_single_span(self.position)) + } + + fn next_char(&mut self) -> Option { + let (position, ch) = self.chars.next()?; + self.position = position as u32; + Some(ch) + } + + fn peek_char(&mut self) -> Option { + self.chars.clone().next().map(|(_, ch)| ch) + } + + fn is_code_whitespace(c: char) -> bool { + c.is_ascii_whitespace() + } +} + +type SpannedTokenResult = Result; + +#[derive(Debug)] +pub(crate) enum LexerError { + UnexpectedCharacter { char: char, span: Span }, +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs new file mode 100644 index 00000000000..ab5302ef94a --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -0,0 +1,19 @@ +#![cfg(test)] + +use crate::ssa::Ssa; + +fn assert_ssa_roundtrip(src: &str) { + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.to_string().trim(), src.trim()); +} + +#[test] +fn test_empty_function() { + let src = " +acir(inline) fn main f0 { + b0(): + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs new file mode 100644 index 00000000000..49e28bbbd74 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -0,0 +1,77 @@ +use noirc_errors::{Position, Span, Spanned}; + +pub(crate) struct SpannedToken(Spanned); + +impl SpannedToken { + pub(crate) fn new(token: Token, span: Span) -> SpannedToken { + SpannedToken(Spanned::from(span, token)) + } + + pub(crate) fn to_span(&self) -> Span { + self.0.span() + } + + pub(crate) fn token(&self) -> &Token { + &self.0.contents + } + + pub(crate) fn into_token(self) -> Token { + self.0.contents + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) enum Token { + Ident(String), + Keyword(Keyword), + /// ( + LeftParen, + /// ) + RightParen, + /// { + LeftBrace, + /// } + RightBrace, + /// : + Colon, + Eof, +} + +impl Token { + pub(super) fn into_single_span(self, position: Position) -> SpannedToken { + self.into_span(position, position) + } + + pub(super) fn into_span(self, start: Position, end: Position) -> SpannedToken { + SpannedToken(Spanned::from_position(start, end, self)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) enum Keyword { + Acir, + Brillig, + Inline, + InlineAlways, + Fold, + Fn, + NoPredicates, + Return, +} + +impl Keyword { + pub(crate) fn lookup_keyword(word: &str) -> Option { + let keyword = match word { + "acir" => Keyword::Acir, + "brillig" => Keyword::Brillig, + "inline" => Keyword::Inline, + "inline_always" => Keyword::InlineAlways, + "fold" => Keyword::Fold, + "fn" => Keyword::Fn, + "no_predicates" => Keyword::NoPredicates, + "return" => Keyword::Return, + _ => return None, + }; + Some(Token::Keyword(keyword)) + } +} From c2f91645e21ffe24bd1dc8c4062bedd7a9171095 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 7 Nov 2024 16:14:32 -0300 Subject: [PATCH 04/65] Also test brillig function --- compiler/noirc_evaluator/src/ssa/parser/tests.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index ab5302ef94a..0ed80d58fa0 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -8,7 +8,7 @@ fn assert_ssa_roundtrip(src: &str) { } #[test] -fn test_empty_function() { +fn test_empty_acir_function() { let src = " acir(inline) fn main f0 { b0(): @@ -17,3 +17,14 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_empty_brillig_function() { + let src = " +brillig(inline) fn main f0 { + b0(): + return +} +"; + assert_ssa_roundtrip(src); +} From 70ab9b0fe049fa6dc921f5fec8e759c915b33f30 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 08:36:48 -0300 Subject: [PATCH 05/65] return Field --- Cargo.lock | 1 + compiler/noirc_evaluator/Cargo.toml | 3 +- compiler/noirc_evaluator/src/ssa/parser.rs | 104 ++++++++++++++---- .../noirc_evaluator/src/ssa/parser/ast.rs | 33 ++---- .../src/ssa/parser/into_ssa.rs | 38 +++++++ .../noirc_evaluator/src/ssa/parser/lexer.rs | 85 ++++++++++++-- .../noirc_evaluator/src/ssa/parser/tests.rs | 11 ++ .../noirc_evaluator/src/ssa/parser/token.rs | 8 ++ compiler/noirc_frontend/src/lexer/lexer.rs | 6 +- compiler/noirc_frontend/src/lexer/token.rs | 11 +- 10 files changed, 238 insertions(+), 62 deletions(-) create mode 100644 compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs diff --git a/Cargo.lock b/Cargo.lock index 936aa636e86..506a62b410d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2935,6 +2935,7 @@ dependencies = [ "noirc_errors", "noirc_frontend", "num-bigint", + "num-traits", "proptest", "rayon", "serde", diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index 1db6af2ae85..509cca24e15 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -20,6 +20,7 @@ fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" +num-traits.workspace = true im.workspace = true serde.workspace = true serde_json.workspace = true @@ -34,4 +35,4 @@ proptest.workspace = true [features] bn254 = ["noirc_frontend/bn254"] -bls12_381= [] +bls12_381 = [] diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index f7271cdb223..952f2802bfd 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -1,6 +1,7 @@ -use super::Ssa; +use super::{ir::types::Type, Ssa}; -use ast::{ParsedBlock, ParsedFunction, ParsedSsa}; +use acvm::FieldElement; +use ast::{ParsedBlock, ParsedFunction, ParsedSsa, ParsedValue}; use lexer::{Lexer, LexerError}; use noirc_errors::Span; use noirc_frontend::monomorphization::ast::InlineType; @@ -9,6 +10,7 @@ use token::{Keyword, SpannedToken, Token}; use crate::ssa::{ir::function::RuntimeType, parser::ast::ParsedTerminator}; mod ast; +mod into_ssa; mod lexer; mod tests; mod token; @@ -129,13 +131,42 @@ impl<'a> Parser<'a> { } fn parse_terminator(&mut self) -> ParseResult { - if self.eat_keyword(Keyword::Return)? { - Ok(ParsedTerminator::Return) + if let Some(terminator) = self.parse_return()? { + Ok(terminator) } else { self.expected_instruction_or_terminator() } } + fn parse_return(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::Return)? { + return Ok(None); + } + + let values = self.parse_comma_separated_values()?; + Ok(Some(ParsedTerminator::Return(values))) + } + + fn parse_comma_separated_values(&mut self) -> ParseResult> { + let mut values = Vec::new(); + while let Some(value) = self.parse_value()? { + values.push(value); + if !self.eat(Token::Comma)? { + break; + } + } + Ok(values) + } + + fn parse_value(&mut self) -> ParseResult> { + if self.eat_keyword(Keyword::Field)? { + let constant = self.eat_int_or_error()?; + Ok(Some(ParsedValue::NumericConstant { constant, typ: Type::field() })) + } else { + Ok(None) + } + } + fn eat_keyword(&mut self, keyword: Keyword) -> ParseResult { if let Token::Keyword(kw) = self.token.token() { if *kw == keyword { @@ -169,6 +200,26 @@ impl<'a> Parser<'a> { } } + fn eat_int(&mut self) -> ParseResult> { + if matches!(self.token.token(), Token::Int(..)) { + let token = self.bump()?; + match token.into_token() { + Token::Int(int) => Ok(Some(int)), + _ => unreachable!(), + } + } else { + Ok(None) + } + } + + fn eat_int_or_error(&mut self) -> ParseResult { + if let Some(int) = self.eat_int()? { + Ok(int) + } else { + self.expected_int() + } + } + fn eat(&mut self, token: Token) -> ParseResult { if self.token.token() == &token { self.bump()?; @@ -204,36 +255,51 @@ impl<'a> Parser<'a> { } fn expected_instruction_or_terminator(&mut self) -> ParseResult { - Err(ParserError::ExpectedInstructionOrTerminator( - self.token.token().clone(), - self.token.to_span(), - )) + Err(ParserError::ExpectedInstructionOrTerminator { + found: self.token.token().clone(), + span: self.token.to_span(), + }) } fn expected_identifier(&mut self) -> ParseResult { - Err(ParserError::ExpectedIdentifier(self.token.token().clone(), self.token.to_span())) + Err(ParserError::ExpectedIdentifier { + found: self.token.token().clone(), + span: self.token.to_span(), + }) + } + + fn expected_int(&mut self) -> ParseResult { + Err(ParserError::ExpectedInt { + found: self.token.token().clone(), + span: self.token.to_span(), + }) } fn expected_token(&mut self, token: Token) -> ParseResult { - Err(ParserError::ExpectedToken(token, self.token.token().clone(), self.token.to_span())) + Err(ParserError::ExpectedToken { + token, + found: self.token.token().clone(), + span: self.token.to_span(), + }) } fn expected_one_of_tokens(&mut self, tokens: &[Token]) -> ParseResult { - Err(ParserError::ExpectedOneOfTokens( - tokens.to_vec(), - self.token.token().clone(), - self.token.to_span(), - )) + Err(ParserError::ExpectedOneOfTokens { + tokens: tokens.to_vec(), + found: self.token.token().clone(), + span: self.token.to_span(), + }) } } #[derive(Debug)] pub(crate) enum ParserError { LexerError(LexerError), - ExpectedToken(Token, Token, Span), - ExpectedOneOfTokens(Vec, Token, Span), - ExpectedIdentifier(Token, Span), - ExpectedInstructionOrTerminator(Token, Span), + ExpectedToken { token: Token, found: Token, span: Span }, + ExpectedOneOfTokens { tokens: Vec, found: Token, span: Span }, + ExpectedIdentifier { found: Token, span: Span }, + ExpectedInt { found: Token, span: Span }, + ExpectedInstructionOrTerminator { found: Token, span: Span }, } fn eof_spanned_token() -> SpannedToken { diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 0df8eeb6ff0..9766d65536c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -1,34 +1,12 @@ -use crate::ssa::{ - function_builder::FunctionBuilder, - ir::function::{FunctionId, RuntimeType}, -}; +use acvm::FieldElement; -use super::{Ssa, SsaError}; +use crate::ssa::ir::{function::RuntimeType, types::Type}; #[derive(Debug)] pub(crate) struct ParsedSsa { pub(crate) functions: Vec, } -impl ParsedSsa { - pub(crate) fn into_ssa(mut self) -> Result { - let mut main_function = self.functions.remove(0); - let main_id = FunctionId::new(0); - - let mut builder = FunctionBuilder::new(main_function.external_name, main_id); - builder.set_runtime(main_function.runtime_type); - - let entry_block = main_function.blocks.remove(0); - match entry_block.terminator { - ParsedTerminator::Return => { - builder.terminate_with_return(vec![]); - } - } - - Ok(builder.finish()) - } -} - #[derive(Debug)] pub(crate) struct ParsedFunction { pub(crate) runtime_type: RuntimeType, @@ -49,5 +27,10 @@ pub(crate) enum ParsedInstruction {} #[derive(Debug)] pub(crate) enum ParsedTerminator { - Return, + Return(Vec), +} + +#[derive(Debug)] +pub(crate) enum ParsedValue { + NumericConstant { constant: FieldElement, typ: Type }, } diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs new file mode 100644 index 00000000000..828ebfd8016 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -0,0 +1,38 @@ +use iter_extended::vecmap; + +use crate::ssa::{function_builder::FunctionBuilder, ir::function::FunctionId}; + +use super::{ParsedSsa, ParsedTerminator, ParsedValue, Ssa, SsaError}; + +impl ParsedSsa { + pub(crate) fn into_ssa(self) -> Result { + let mut translator = Translator {}; + translator.translate_parsed_ssa(self) + } +} + +struct Translator {} + +impl Translator { + fn translate_parsed_ssa(&mut self, mut parsed_ssa: ParsedSsa) -> Result { + let mut main_function = parsed_ssa.functions.remove(0); + let main_id = FunctionId::new(0); + + let mut builder = FunctionBuilder::new(main_function.external_name, main_id); + builder.set_runtime(main_function.runtime_type); + + let entry_block = main_function.blocks.remove(0); + match entry_block.terminator { + ParsedTerminator::Return(values) => { + let return_values = vecmap(values, |value| match value { + ParsedValue::NumericConstant { constant, typ } => { + builder.numeric_constant(constant, typ) + } + }); + builder.terminate_with_return(return_values); + } + } + + Ok(builder.finish()) + } +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index 6bcb18aa321..e01f8396ba3 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -1,6 +1,10 @@ -use std::str::CharIndices; +use std::str::{CharIndices, FromStr}; +use acvm::{AcirField, FieldElement}; use noirc_errors::{Position, Span}; +use noirc_frontend::token::IntType; +use num_bigint::BigInt; +use num_traits::{Num, One}; use super::token::{Keyword, SpannedToken, Token}; @@ -8,11 +12,18 @@ pub(crate) struct Lexer<'a> { chars: CharIndices<'a>, position: Position, done: bool, + max_integer: BigInt, } impl<'a> Lexer<'a> { pub(crate) fn new(source: &'a str) -> Self { - Lexer { chars: source.char_indices(), position: 0, done: false } + Lexer { + chars: source.char_indices(), + position: 0, + done: false, + max_integer: BigInt::from_biguint(num_bigint::Sign::Plus, FieldElement::modulus()) + - BigInt::one(), + } } pub(crate) fn next_token(&mut self) -> SpannedTokenResult { @@ -27,6 +38,7 @@ impl<'a> Lexer<'a> { } self.next_token() } + Some(',') => self.single_char_token(Token::Comma), Some(':') => self.single_char_token(Token::Colon), Some('(') => self.single_char_token(Token::LeftParen), Some(')') => self.single_char_token(Token::RightParen), @@ -47,7 +59,7 @@ impl<'a> Lexer<'a> { fn eat_alpha_numeric(&mut self, initial_char: char) -> SpannedTokenResult { match initial_char { 'A'..='Z' | 'a'..='z' | '_' => Ok(self.eat_word(initial_char)?), - // '0'..='9' => self.eat_digit(initial_char), + '0'..='9' => self.eat_digit(initial_char), _ => Err(LexerError::UnexpectedCharacter { char: initial_char, span: Span::single_char(self.position), @@ -81,18 +93,73 @@ impl<'a> Lexer<'a> { // Check if word an int type // if no error occurred, then it is either a valid integer type or it is not an int type - // let parsed_token = IntType::lookup_int_type(&word)?; + let parsed_token = IntType::lookup_int_type(&word); - // // Check if it is an int type - // if let Some(int_type_token) = parsed_token { - // return Ok(int_type_token.into_span(start, end)); - // } + // Check if it is an int type + if let Some(int_type) = parsed_token { + return Ok(Token::IntType(int_type).into_span(start, end)); + } // Else it is just an identifier let ident_token = Token::Ident(word); Ok(ident_token.into_span(start, end)) } + fn eat_digit(&mut self, initial_char: char) -> SpannedTokenResult { + let start = self.position; + + let integer_str = self.eat_while(Some(initial_char), |ch| { + ch.is_ascii_digit() | ch.is_ascii_hexdigit() | (ch == 'x') | (ch == '_') + }); + + let end = self.position; + + // We want to enforce some simple rules about usage of underscores: + // 1. Underscores cannot appear at the end of a integer literal. e.g. 0x123_. + // 2. There cannot be more than one underscore consecutively, e.g. 0x5__5, 5__5. + // + // We're not concerned with an underscore at the beginning of a decimal literal + // such as `_5` as this would be lexed into an ident rather than an integer literal. + let invalid_underscore_location = integer_str.ends_with('_'); + let consecutive_underscores = integer_str.contains("__"); + if invalid_underscore_location || consecutive_underscores { + return Err(LexerError::InvalidIntegerLiteral { + span: Span::inclusive(start, end), + found: integer_str, + }); + } + + // Underscores needs to be stripped out before the literal can be converted to a `FieldElement. + let integer_str = integer_str.replace('_', ""); + + let bigint_result = match integer_str.strip_prefix("0x") { + Some(integer_str) => BigInt::from_str_radix(integer_str, 16), + None => BigInt::from_str(&integer_str), + }; + + let integer = match bigint_result { + Ok(bigint) => { + if bigint > self.max_integer { + return Err(LexerError::IntegerLiteralTooLarge { + span: Span::inclusive(start, end), + limit: self.max_integer.to_string(), + }); + } + let big_uint = bigint.magnitude(); + FieldElement::from_be_bytes_reduce(&big_uint.to_bytes_be()) + } + Err(_) => { + return Err(LexerError::InvalidIntegerLiteral { + span: Span::inclusive(start, end), + found: integer_str, + }) + } + }; + + let integer_token = Token::Int(integer); + Ok(integer_token.into_span(start, end)) + } + fn eat_while bool>( &mut self, initial_char: Option, @@ -147,4 +214,6 @@ type SpannedTokenResult = Result; #[derive(Debug)] pub(crate) enum LexerError { UnexpectedCharacter { char: char, span: Span }, + InvalidIntegerLiteral { span: Span, found: String }, + IntegerLiteralTooLarge { span: Span, limit: String }, } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 0ed80d58fa0..4f67dde2ba1 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -28,3 +28,14 @@ brillig(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_return_field() { + let src = " +acir(inline) fn main f0 { + b0(): + return Field 1 +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 49e28bbbd74..0b7b64bb930 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -1,4 +1,6 @@ +use acvm::FieldElement; use noirc_errors::{Position, Span, Spanned}; +use noirc_frontend::token::IntType; pub(crate) struct SpannedToken(Spanned); @@ -23,7 +25,9 @@ impl SpannedToken { #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Token { Ident(String), + Int(FieldElement), Keyword(Keyword), + IntType(IntType), /// ( LeftParen, /// ) @@ -32,6 +36,8 @@ pub(crate) enum Token { LeftBrace, /// } RightBrace, + /// , + Comma, /// : Colon, Eof, @@ -53,6 +59,7 @@ pub(crate) enum Keyword { Brillig, Inline, InlineAlways, + Field, Fold, Fn, NoPredicates, @@ -66,6 +73,7 @@ impl Keyword { "brillig" => Keyword::Brillig, "inline" => Keyword::Inline, "inline_always" => Keyword::InlineAlways, + "Field" => Keyword::Field, "fold" => Keyword::Fold, "fn" => Keyword::Fn, "no_predicates" => Keyword::NoPredicates, diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index b99fd993425..68dc142ff10 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -340,11 +340,11 @@ impl<'a> Lexer<'a> { // Check if word an int type // if no error occurred, then it is either a valid integer type or it is not an int type - let parsed_token = IntType::lookup_int_type(&word)?; + let parsed_token = IntType::lookup_int_type(&word); // Check if it is an int type - if let Some(int_type_token) = parsed_token { - return Ok(int_type_token.into_span(start, end)); + if let Some(int_type) = parsed_token { + return Ok(Token::IntType(int_type).into_span(start, end)); } // Else it is just an identifier diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 17efdecf6ca..bf0e675e670 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -4,7 +4,6 @@ use std::fmt::{self, Display}; use crate::{ ast::{Expression, Path}, - lexer::errors::LexerErrorKind, node_interner::{ ExprId, InternedExpressionKind, InternedPattern, InternedStatementKind, InternedUnresolvedTypeData, QuotedTypeId, @@ -598,7 +597,7 @@ impl IntType { // XXX: Result // Is not the best API. We could split this into two functions. One that checks if the // word is a integer, which only returns an Option - pub(crate) fn lookup_int_type(word: &str) -> Result, LexerErrorKind> { + pub fn lookup_int_type(word: &str) -> Option { // Check if the first string is a 'u' or 'i' let is_signed = if word.starts_with('i') { @@ -606,20 +605,20 @@ impl IntType { } else if word.starts_with('u') { false } else { - return Ok(None); + return None; }; // Word start with 'u' or 'i'. Check if the latter is an integer let str_as_u32 = match word[1..].parse::() { Ok(str_as_u32) => str_as_u32, - Err(_) => return Ok(None), + Err(_) => return None, }; if is_signed { - Ok(Some(Token::IntType(IntType::Signed(str_as_u32)))) + Some(IntType::Signed(str_as_u32)) } else { - Ok(Some(Token::IntType(IntType::Unsigned(str_as_u32)))) + Some(IntType::Unsigned(str_as_u32)) } } } From 6bb0bcc8a3d1c91ccf82ff5e8e7b7868c932c64d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 09:45:08 -0300 Subject: [PATCH 06/65] Parse all integer types --- compiler/noirc_evaluator/src/ssa/parser.rs | 22 ++++++++++++++++++- .../noirc_evaluator/src/ssa/parser/tests.rs | 18 +++++++++------ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 952f2802bfd..d257ac83961 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -4,7 +4,7 @@ use acvm::FieldElement; use ast::{ParsedBlock, ParsedFunction, ParsedSsa, ParsedValue}; use lexer::{Lexer, LexerError}; use noirc_errors::Span; -use noirc_frontend::monomorphization::ast::InlineType; +use noirc_frontend::{monomorphization::ast::InlineType, token::IntType}; use token::{Keyword, SpannedToken, Token}; use crate::ssa::{ir::function::RuntimeType, parser::ast::ParsedTerminator}; @@ -162,6 +162,13 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Field)? { let constant = self.eat_int_or_error()?; Ok(Some(ParsedValue::NumericConstant { constant, typ: Type::field() })) + } else if let Some(int_type) = self.eat_int_type()? { + let constant = self.eat_int_or_error()?; + let typ = match int_type { + IntType::Unsigned(bit_size) => Type::unsigned(bit_size), + IntType::Signed(bit_size) => Type::signed(bit_size), + }; + Ok(Some(ParsedValue::NumericConstant { constant, typ })) } else { Ok(None) } @@ -220,6 +227,19 @@ impl<'a> Parser<'a> { } } + fn eat_int_type(&mut self) -> ParseResult> { + let is_int_type = matches!(self.token.token(), Token::IntType(..)); + if is_int_type { + let token = self.bump()?; + match token.into_token() { + Token::IntType(int_type) => Ok(Some(int_type)), + _ => unreachable!(), + } + } else { + Ok(None) + } + } + fn eat(&mut self, token: Token) -> ParseResult { if self.token.token() == &token { self.bump()?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 4f67dde2ba1..78967ed1c4c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -30,12 +30,16 @@ brillig(inline) fn main f0 { } #[test] -fn test_return_field() { - let src = " -acir(inline) fn main f0 { +fn test_return_integer() { + for typ in ["u1", "u8", "u16", "u32", "u64", "i1", "i8", "i16", "i32", "i64", "Field"] { + let src = format!( + " +acir(inline) fn main f0 {{ b0(): - return Field 1 -} -"; - assert_ssa_roundtrip(src); + return {typ} 1 +}} +" + ); + assert_ssa_roundtrip(&src); + } } From a69145f5b220351a32168115076ad3681c4c536b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 10:31:01 -0300 Subject: [PATCH 07/65] Arrays --- .../noirc_evaluator/src/ssa/ir/printer.rs | 11 ++- compiler/noirc_evaluator/src/ssa/parser.rs | 72 ++++++++++++++++++- .../noirc_evaluator/src/ssa/parser/ast.rs | 1 + .../src/ssa/parser/into_ssa.rs | 65 ++++++++++++----- .../noirc_evaluator/src/ssa/parser/lexer.rs | 2 + .../noirc_evaluator/src/ssa/parser/tests.rs | 33 +++++++++ .../noirc_evaluator/src/ssa/parser/token.rs | 6 ++ 7 files changed, 168 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 5816af8f426..464aa8ec540 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -70,9 +70,16 @@ fn value(function: &Function, id: ValueId) -> String { } Value::Function(id) => id.to_string(), Value::Intrinsic(intrinsic) => intrinsic.to_string(), - Value::Array { array, .. } => { + Value::Array { array, typ } => { let elements = vecmap(array, |element| value(function, *element)); - format!("[{}]", elements.join(", ")) + let element_types = &typ.clone().element_types(); + let element_types_str = + element_types.iter().map(|typ| typ.to_string()).collect::>().join(", "); + if element_types.len() == 1 { + format!("[{}] of {}", elements.join(", "), element_types_str) + } else { + format!("[{}] of ({})", elements.join(", "), element_types_str) + } } Value::Param { .. } | Value::Instruction { .. } | Value::ForeignFunction(_) => { id.to_string() diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index d257ac83961..2e5d05171fb 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use super::{ir::types::Type, Ssa}; use acvm::FieldElement; @@ -159,10 +161,32 @@ impl<'a> Parser<'a> { } fn parse_value(&mut self) -> ParseResult> { + if let Some(value) = self.parse_field_value()? { + return Ok(Some(value)); + } + + if let Some(value) = self.parse_int_value()? { + return Ok(Some(value)); + } + + if let Some(value) = self.parse_array_value()? { + return Ok(Some(value)); + } + + Ok(None) + } + + fn parse_field_value(&mut self) -> ParseResult> { if self.eat_keyword(Keyword::Field)? { let constant = self.eat_int_or_error()?; Ok(Some(ParsedValue::NumericConstant { constant, typ: Type::field() })) - } else if let Some(int_type) = self.eat_int_type()? { + } else { + Ok(None) + } + } + + fn parse_int_value(&mut self) -> ParseResult> { + if let Some(int_type) = self.eat_int_type()? { let constant = self.eat_int_or_error()?; let typ = match int_type { IntType::Unsigned(bit_size) => Type::unsigned(bit_size), @@ -174,6 +198,44 @@ impl<'a> Parser<'a> { } } + fn parse_array_value(&mut self) -> ParseResult> { + if self.eat(Token::LeftBracket)? { + let values = self.parse_comma_separated_values()?; + self.eat_or_error(Token::RightBracket)?; + self.eat_or_error(Token::Keyword(Keyword::Of))?; + let types = if self.eat(Token::LeftParen)? { + let types = self.parse_comma_separated_types()?; + self.eat_or_error(Token::RightParen)?; + types + } else { + vec![self.parse_type()?] + }; + Ok(Some(ParsedValue::Array { typ: Type::Array(Arc::new(types), values.len()), values })) + } else { + Ok(None) + } + } + + fn parse_comma_separated_types(&mut self) -> ParseResult> { + let mut types = Vec::new(); + loop { + let typ = self.parse_type()?; + types.push(typ); + if !self.eat(Token::Comma)? { + break; + } + } + Ok(types) + } + + fn parse_type(&mut self) -> ParseResult { + if self.eat_keyword(Keyword::Field)? { + return Ok(Type::field()); + } + + self.expected_type() + } + fn eat_keyword(&mut self, keyword: Keyword) -> ParseResult { if let Token::Keyword(kw) = self.token.token() { if *kw == keyword { @@ -295,6 +357,13 @@ impl<'a> Parser<'a> { }) } + fn expected_type(&mut self) -> ParseResult { + Err(ParserError::ExpectedType { + found: self.token.token().clone(), + span: self.token.to_span(), + }) + } + fn expected_token(&mut self, token: Token) -> ParseResult { Err(ParserError::ExpectedToken { token, @@ -319,6 +388,7 @@ pub(crate) enum ParserError { ExpectedOneOfTokens { tokens: Vec, found: Token, span: Span }, ExpectedIdentifier { found: Token, span: Span }, ExpectedInt { found: Token, span: Span }, + ExpectedType { found: Token, span: Span }, ExpectedInstructionOrTerminator { found: Token, span: Span }, } diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 9766d65536c..4b46c637b82 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -33,4 +33,5 @@ pub(crate) enum ParsedTerminator { #[derive(Debug)] pub(crate) enum ParsedValue { NumericConstant { constant: FieldElement, typ: Type }, + Array { values: Vec, typ: Type }, } diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 828ebfd8016..890a943beec 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -1,38 +1,65 @@ -use iter_extended::vecmap; +use im::Vector; -use crate::ssa::{function_builder::FunctionBuilder, ir::function::FunctionId}; +use crate::ssa::{ + function_builder::FunctionBuilder, + ir::{function::FunctionId, value::ValueId}, +}; -use super::{ParsedSsa, ParsedTerminator, ParsedValue, Ssa, SsaError}; +use super::{ParsedFunction, ParsedSsa, ParsedTerminator, ParsedValue, Ssa, SsaError}; impl ParsedSsa { - pub(crate) fn into_ssa(self) -> Result { - let mut translator = Translator {}; - translator.translate_parsed_ssa(self) + pub(crate) fn into_ssa(mut self) -> Result { + let translator = Translator::new(&mut self)?; + Ok(translator.finish()) } } -struct Translator {} +struct Translator { + builder: FunctionBuilder, +} impl Translator { - fn translate_parsed_ssa(&mut self, mut parsed_ssa: ParsedSsa) -> Result { - let mut main_function = parsed_ssa.functions.remove(0); + fn new(parsed_ssa: &mut ParsedSsa) -> Result { + let main_function = parsed_ssa.functions.remove(0); let main_id = FunctionId::new(0); - - let mut builder = FunctionBuilder::new(main_function.external_name, main_id); + let mut builder = FunctionBuilder::new(main_function.external_name.clone(), main_id); builder.set_runtime(main_function.runtime_type); - let entry_block = main_function.blocks.remove(0); + let mut translator = Self { builder }; + translator.translate_function_body(main_function)?; + Ok(translator) + } + + fn translate_function_body(&mut self, mut function: ParsedFunction) -> Result<(), SsaError> { + let entry_block = function.blocks.remove(0); match entry_block.terminator { ParsedTerminator::Return(values) => { - let return_values = vecmap(values, |value| match value { - ParsedValue::NumericConstant { constant, typ } => { - builder.numeric_constant(constant, typ) - } - }); - builder.terminate_with_return(return_values); + let mut return_values = Vec::with_capacity(values.len()); + for value in values { + return_values.push(self.translate_value(value)?); + } + self.builder.terminate_with_return(return_values); + } + } + Ok(()) + } + + fn translate_value(&mut self, value: ParsedValue) -> Result { + match value { + ParsedValue::NumericConstant { constant, typ } => { + Ok(self.builder.numeric_constant(constant, typ)) + } + ParsedValue::Array { values, typ } => { + let mut translated_values = Vector::new(); + for value in values { + translated_values.push_back(self.translate_value(value)?); + } + Ok(self.builder.array_constant(translated_values, typ)) } } + } - Ok(builder.finish()) + fn finish(self) -> Ssa { + self.builder.finish() } } diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index e01f8396ba3..9684d577428 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -44,6 +44,8 @@ impl<'a> Lexer<'a> { Some(')') => self.single_char_token(Token::RightParen), Some('{') => self.single_char_token(Token::LeftBrace), Some('}') => self.single_char_token(Token::RightBrace), + Some('[') => self.single_char_token(Token::LeftBracket), + Some(']') => self.single_char_token(Token::RightBracket), Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch), Some(char) => Err(LexerError::UnexpectedCharacter { char, diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 78967ed1c4c..0f84a87f030 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -43,3 +43,36 @@ acir(inline) fn main f0 {{ assert_ssa_roundtrip(&src); } } + +#[test] +fn test_return_array() { + let src = " +acir(inline) fn main f0 { + b0(): + return [Field 1] of Field +} +"; + assert_ssa_roundtrip(src); +} + +#[test] +fn test_return_empty_array() { + let src = " +acir(inline) fn main f0 { + b0(): + return [] of Field +} +"; + assert_ssa_roundtrip(src); +} + +#[test] +fn test_return_composite_array() { + let src = " +acir(inline) fn main f0 { + b0(): + return [Field 1, Field 2] of (Field, Field) +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 0b7b64bb930..eb88245945c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -36,6 +36,10 @@ pub(crate) enum Token { LeftBrace, /// } RightBrace, + /// [ + LeftBracket, + /// ] + RightBracket, /// , Comma, /// : @@ -63,6 +67,7 @@ pub(crate) enum Keyword { Fold, Fn, NoPredicates, + Of, Return, } @@ -77,6 +82,7 @@ impl Keyword { "fold" => Keyword::Fold, "fn" => Keyword::Fn, "no_predicates" => Keyword::NoPredicates, + "of" => Keyword::Of, "return" => Keyword::Return, _ => return None, }; From b7dba51675a167acc1aac7e17352ff131e82cd61 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 10:47:24 -0300 Subject: [PATCH 08/65] Block parameters and variables --- compiler/noirc_evaluator/src/ssa/parser.rs | 25 +++++++++++++++++-- .../noirc_evaluator/src/ssa/parser/ast.rs | 8 ++++++ .../src/ssa/parser/into_ssa.rs | 19 +++++++++++++- .../noirc_evaluator/src/ssa/parser/tests.rs | 11 ++++++++ .../noirc_evaluator/src/ssa/parser/token.rs | 1 + 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 2e5d05171fb..59cdbe1c554 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use super::{ir::types::Type, Ssa}; use acvm::FieldElement; -use ast::{ParsedBlock, ParsedFunction, ParsedSsa, ParsedValue}; +use ast::{ParsedBlock, ParsedFunction, ParsedParameter, ParsedSsa, ParsedValue}; use lexer::{Lexer, LexerError}; use noirc_errors::Span; use noirc_frontend::{monomorphization::ast::InlineType, token::IntType}; @@ -28,6 +28,7 @@ impl Ssa { #[derive(Debug)] pub(crate) enum SsaError { ParserError(ParserError), + UnknownVariable(String), } type ParseResult = Result; @@ -124,12 +125,28 @@ impl<'a> Parser<'a> { fn parse_block(&mut self) -> ParseResult { let name = self.eat_ident_or_error()?; self.eat_or_error(Token::LeftParen)?; + + let mut parameters = Vec::new(); + while !self.at(Token::RightParen) { + parameters.push(self.parse_parameter()?); + if !self.eat(Token::Comma)? { + break; + } + } + self.eat_or_error(Token::RightParen)?; self.eat_or_error(Token::Colon)?; let instructions = Vec::new(); let terminator = self.parse_terminator()?; - Ok(ParsedBlock { name, instructions, terminator }) + Ok(ParsedBlock { name, parameters, instructions, terminator }) + } + + fn parse_parameter(&mut self) -> ParseResult { + let name = self.eat_ident_or_error()?; + self.eat_or_error(Token::Colon)?; + let typ = self.parse_type()?; + Ok(ParsedParameter { name, typ }) } fn parse_terminator(&mut self) -> ParseResult { @@ -173,6 +190,10 @@ impl<'a> Parser<'a> { return Ok(Some(value)); } + if let Some(name) = self.eat_ident()? { + return Ok(Some(ParsedValue::Variable(name))); + } + Ok(None) } diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 4b46c637b82..fd36c1f9690 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -18,10 +18,17 @@ pub(crate) struct ParsedFunction { #[derive(Debug)] pub(crate) struct ParsedBlock { pub(crate) name: String, + pub(crate) parameters: Vec, pub(crate) instructions: Vec, pub(crate) terminator: ParsedTerminator, } +#[derive(Debug)] +pub(crate) struct ParsedParameter { + pub(crate) name: String, + pub(crate) typ: Type, +} + #[derive(Debug)] pub(crate) enum ParsedInstruction {} @@ -34,4 +41,5 @@ pub(crate) enum ParsedTerminator { pub(crate) enum ParsedValue { NumericConstant { constant: FieldElement, typ: Type }, Array { values: Vec, typ: Type }, + Variable(String), } diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 890a943beec..2d5a50cd719 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use im::Vector; use crate::ssa::{ @@ -16,6 +18,9 @@ impl ParsedSsa { struct Translator { builder: FunctionBuilder, + + // Maps parameter names to their value IDs + parameters: HashMap, } impl Translator { @@ -25,13 +30,18 @@ impl Translator { let mut builder = FunctionBuilder::new(main_function.external_name.clone(), main_id); builder.set_runtime(main_function.runtime_type); - let mut translator = Self { builder }; + let mut translator = Self { builder, parameters: HashMap::new() }; translator.translate_function_body(main_function)?; Ok(translator) } fn translate_function_body(&mut self, mut function: ParsedFunction) -> Result<(), SsaError> { let entry_block = function.blocks.remove(0); + for parameter in entry_block.parameters { + let parameter_value_id = self.builder.add_parameter(parameter.typ); + self.parameters.insert(parameter.name, parameter_value_id); + } + match entry_block.terminator { ParsedTerminator::Return(values) => { let mut return_values = Vec::with_capacity(values.len()); @@ -56,6 +66,13 @@ impl Translator { } Ok(self.builder.array_constant(translated_values, typ)) } + ParsedValue::Variable(name) => { + if let Some(value_id) = self.parameters.get(&name) { + Ok(*value_id) + } else { + Err(SsaError::UnknownVariable(name)) + } + } } } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 0f84a87f030..27430a0e1b0 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -76,3 +76,14 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_block_parameters() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field, v1: Field): + return v0, v1 +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index eb88245945c..d4743b2c95a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -2,6 +2,7 @@ use acvm::FieldElement; use noirc_errors::{Position, Span, Spanned}; use noirc_frontend::token::IntType; +#[derive(Debug)] pub(crate) struct SpannedToken(Spanned); impl SpannedToken { From 30b49ef0186570bb12d55af2762c2ce4c8276dbd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 10:51:01 -0300 Subject: [PATCH 09/65] Carry spans in identifiers --- compiler/noirc_evaluator/src/ssa/parser.rs | 10 ++++++---- compiler/noirc_evaluator/src/ssa/parser/ast.rs | 17 +++++++++++++++-- .../noirc_evaluator/src/ssa/parser/into_ssa.rs | 8 ++++---- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 59cdbe1c554..594a77c8cf3 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use super::{ir::types::Type, Ssa}; use acvm::FieldElement; -use ast::{ParsedBlock, ParsedFunction, ParsedParameter, ParsedSsa, ParsedValue}; +use ast::{Identifier, ParsedBlock, ParsedFunction, ParsedParameter, ParsedSsa, ParsedValue}; use lexer::{Lexer, LexerError}; use noirc_errors::Span; use noirc_frontend::{monomorphization::ast::InlineType, token::IntType}; @@ -28,7 +28,7 @@ impl Ssa { #[derive(Debug)] pub(crate) enum SsaError { ParserError(ParserError), - UnknownVariable(String), + UnknownVariable(Identifier), } type ParseResult = Result; @@ -143,10 +143,11 @@ impl<'a> Parser<'a> { } fn parse_parameter(&mut self) -> ParseResult { + let span = self.token.to_span(); let name = self.eat_ident_or_error()?; self.eat_or_error(Token::Colon)?; let typ = self.parse_type()?; - Ok(ParsedParameter { name, typ }) + Ok(ParsedParameter { identifier: Identifier::new(name, span), typ }) } fn parse_terminator(&mut self) -> ParseResult { @@ -190,8 +191,9 @@ impl<'a> Parser<'a> { return Ok(Some(value)); } + let span = self.token.to_span(); if let Some(name) = self.eat_ident()? { - return Ok(Some(ParsedValue::Variable(name))); + return Ok(Some(ParsedValue::Variable(Identifier::new(name, span)))); } Ok(None) diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index fd36c1f9690..2e21c56918f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -1,4 +1,5 @@ use acvm::FieldElement; +use noirc_errors::Span; use crate::ssa::ir::{function::RuntimeType, types::Type}; @@ -25,10 +26,22 @@ pub(crate) struct ParsedBlock { #[derive(Debug)] pub(crate) struct ParsedParameter { - pub(crate) name: String, + pub(crate) identifier: Identifier, pub(crate) typ: Type, } +#[derive(Debug)] +pub(crate) struct Identifier { + pub(crate) name: String, + pub(crate) span: Span, +} + +impl Identifier { + pub(crate) fn new(name: String, span: Span) -> Self { + Self { name, span } + } +} + #[derive(Debug)] pub(crate) enum ParsedInstruction {} @@ -41,5 +54,5 @@ pub(crate) enum ParsedTerminator { pub(crate) enum ParsedValue { NumericConstant { constant: FieldElement, typ: Type }, Array { values: Vec, typ: Type }, - Variable(String), + Variable(Identifier), } diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 2d5a50cd719..b0d9f44e232 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -39,7 +39,7 @@ impl Translator { let entry_block = function.blocks.remove(0); for parameter in entry_block.parameters { let parameter_value_id = self.builder.add_parameter(parameter.typ); - self.parameters.insert(parameter.name, parameter_value_id); + self.parameters.insert(parameter.identifier.name, parameter_value_id); } match entry_block.terminator { @@ -66,11 +66,11 @@ impl Translator { } Ok(self.builder.array_constant(translated_values, typ)) } - ParsedValue::Variable(name) => { - if let Some(value_id) = self.parameters.get(&name) { + ParsedValue::Variable(identifier) => { + if let Some(value_id) = self.parameters.get(&identifier.name) { Ok(*value_id) } else { - Err(SsaError::UnknownVariable(name)) + Err(SsaError::UnknownVariable(identifier)) } } } From 994dc3813d719494866b232507198f4b2650b44e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 11:20:15 -0300 Subject: [PATCH 10/65] Blocks and jmp --- compiler/noirc_evaluator/src/ssa/parser.rs | 51 +++++++++--- .../noirc_evaluator/src/ssa/parser/ast.rs | 1 + .../src/ssa/parser/into_ssa.rs | 77 +++++++++++++++---- .../noirc_evaluator/src/ssa/parser/tests.rs | 21 ++++- .../noirc_evaluator/src/ssa/parser/token.rs | 2 + 5 files changed, 128 insertions(+), 24 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 594a77c8cf3..b71025c45e6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -29,6 +29,7 @@ impl Ssa { pub(crate) enum SsaError { ParserError(ParserError), UnknownVariable(Identifier), + UnknownBlock(Identifier), } type ParseResult = Result; @@ -143,19 +144,39 @@ impl<'a> Parser<'a> { } fn parse_parameter(&mut self) -> ParseResult { - let span = self.token.to_span(); - let name = self.eat_ident_or_error()?; + let identifier = self.parse_identifier_or_error()?; self.eat_or_error(Token::Colon)?; let typ = self.parse_type()?; - Ok(ParsedParameter { identifier: Identifier::new(name, span), typ }) + Ok(ParsedParameter { identifier, typ }) + } + + fn parse_identifier_or_error(&mut self) -> ParseResult { + if let Some(identifier) = self.parse_identifier()? { + Ok(identifier) + } else { + self.expected_identifier() + } + } + + fn parse_identifier(&mut self) -> ParseResult> { + let span = self.token.to_span(); + if let Some(name) = self.eat_ident()? { + Ok(Some(Identifier::new(name, span))) + } else { + Ok(None) + } } fn parse_terminator(&mut self) -> ParseResult { if let Some(terminator) = self.parse_return()? { - Ok(terminator) - } else { - self.expected_instruction_or_terminator() + return Ok(terminator); + } + + if let Some(terminator) = self.parse_jmp()? { + return Ok(terminator); } + + self.expected_instruction_or_terminator() } fn parse_return(&mut self) -> ParseResult> { @@ -167,6 +188,19 @@ impl<'a> Parser<'a> { Ok(Some(ParsedTerminator::Return(values))) } + fn parse_jmp(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::Jmp)? { + return Ok(None); + } + + let destination = self.parse_identifier_or_error()?; + self.eat_or_error(Token::LeftParen)?; + let arguments = self.parse_comma_separated_values()?; + self.eat_or_error(Token::RightParen)?; + + Ok(Some(ParsedTerminator::Jmp { destination, arguments })) + } + fn parse_comma_separated_values(&mut self) -> ParseResult> { let mut values = Vec::new(); while let Some(value) = self.parse_value()? { @@ -191,9 +225,8 @@ impl<'a> Parser<'a> { return Ok(Some(value)); } - let span = self.token.to_span(); - if let Some(name) = self.eat_ident()? { - return Ok(Some(ParsedValue::Variable(Identifier::new(name, span)))); + if let Some(identifier) = self.parse_identifier()? { + return Ok(Some(ParsedValue::Variable(identifier))); } Ok(None) diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 2e21c56918f..168d859d518 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -47,6 +47,7 @@ pub(crate) enum ParsedInstruction {} #[derive(Debug)] pub(crate) enum ParsedTerminator { + Jmp { destination: Identifier, arguments: Vec }, Return(Vec), } diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index b0d9f44e232..fb465f9f7dd 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -4,10 +4,13 @@ use im::Vector; use crate::ssa::{ function_builder::FunctionBuilder, - ir::{function::FunctionId, value::ValueId}, + ir::{basic_block::BasicBlockId, function::FunctionId, value::ValueId}, }; -use super::{ParsedFunction, ParsedSsa, ParsedTerminator, ParsedValue, Ssa, SsaError}; +use super::{ + Identifier, ParsedBlock, ParsedFunction, ParsedSsa, ParsedTerminator, ParsedValue, Ssa, + SsaError, +}; impl ParsedSsa { pub(crate) fn into_ssa(mut self) -> Result { @@ -19,7 +22,10 @@ impl ParsedSsa { struct Translator { builder: FunctionBuilder, - // Maps parameter names to their value IDs + /// Maps block names to their IDs + blocks: HashMap, + + /// Maps parameter names to their IDs parameters: HashMap, } @@ -30,19 +36,51 @@ impl Translator { let mut builder = FunctionBuilder::new(main_function.external_name.clone(), main_id); builder.set_runtime(main_function.runtime_type); - let mut translator = Self { builder, parameters: HashMap::new() }; + let mut translator = Self { builder, parameters: HashMap::new(), blocks: HashMap::new() }; translator.translate_function_body(main_function)?; Ok(translator) } fn translate_function_body(&mut self, mut function: ParsedFunction) -> Result<(), SsaError> { + // First define all blocks so that they are known (a block might jump to a block that comes next) let entry_block = function.blocks.remove(0); - for parameter in entry_block.parameters { - let parameter_value_id = self.builder.add_parameter(parameter.typ); + let entry_block_id = self.builder.current_function.entry_block(); + self.blocks.insert(entry_block.name.clone(), entry_block_id); + + for block in &function.blocks { + let block_id = self.builder.insert_block(); + self.blocks.insert(block.name.clone(), block_id); + } + + self.translate_block(entry_block)?; + + for block in function.blocks { + self.translate_block(block)?; + } + + Ok(()) + } + + fn translate_block(&mut self, block: ParsedBlock) -> Result<(), SsaError> { + let block_id = self.blocks[&block.name]; + self.builder.switch_to_block(block_id); + + for parameter in block.parameters { + let parameter_value_id = self.builder.add_block_parameter(block_id, parameter.typ); self.parameters.insert(parameter.identifier.name, parameter_value_id); } - match entry_block.terminator { + match block.terminator { + ParsedTerminator::Jmp { destination, arguments } => { + let block_id = self.lookup_block(destination)?; + + let mut translated_arguments = Vec::with_capacity(arguments.len()); + for value in arguments { + translated_arguments.push(self.translate_value(value)?); + } + + self.builder.terminate_with_jmp(block_id, translated_arguments); + } ParsedTerminator::Return(values) => { let mut return_values = Vec::with_capacity(values.len()); for value in values { @@ -51,6 +89,7 @@ impl Translator { self.builder.terminate_with_return(return_values); } } + Ok(()) } @@ -66,13 +105,23 @@ impl Translator { } Ok(self.builder.array_constant(translated_values, typ)) } - ParsedValue::Variable(identifier) => { - if let Some(value_id) = self.parameters.get(&identifier.name) { - Ok(*value_id) - } else { - Err(SsaError::UnknownVariable(identifier)) - } - } + ParsedValue::Variable(identifier) => self.lookup_variable(identifier), + } + } + + fn lookup_variable(&mut self, identifier: Identifier) -> Result { + if let Some(value_id) = self.parameters.get(&identifier.name) { + Ok(*value_id) + } else { + Err(SsaError::UnknownVariable(identifier)) + } + } + + fn lookup_block(&mut self, identifier: Identifier) -> Result { + if let Some(block_id) = self.blocks.get(&identifier.name) { + Ok(*block_id) + } else { + Err(SsaError::UnknownBlock(identifier)) } } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 27430a0e1b0..1377135021f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -4,7 +4,13 @@ use crate::ssa::Ssa; fn assert_ssa_roundtrip(src: &str) { let ssa = Ssa::from_str(src).unwrap(); - assert_eq!(ssa.to_string().trim(), src.trim()); + let ssa = ssa.to_string(); + let ssa = ssa.trim(); + let src = src.trim(); + if ssa != src { + println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", src, ssa); + assert_eq!(ssa, src); + } } #[test] @@ -87,3 +93,16 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_multiple_blocks_and_jmp() { + let src: &str = " +acir(inline) fn main f0 { + b0(): + jmp b1(Field 1) + b1(v1: Field): + return v1 +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index d4743b2c95a..5274cee8bee 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -67,6 +67,7 @@ pub(crate) enum Keyword { Field, Fold, Fn, + Jmp, NoPredicates, Of, Return, @@ -82,6 +83,7 @@ impl Keyword { "Field" => Keyword::Field, "fold" => Keyword::Fold, "fn" => Keyword::Fn, + "jmp" => Keyword::Jmp, "no_predicates" => Keyword::NoPredicates, "of" => Keyword::Of, "return" => Keyword::Return, From fb5c8b68742823710b042e9a64808851a71714ab Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 11:33:48 -0300 Subject: [PATCH 11/65] jmpif --- compiler/noirc_evaluator/src/ssa/parser.rs | 37 +++++++++++++++++++ .../noirc_evaluator/src/ssa/parser/ast.rs | 1 + .../src/ssa/parser/into_ssa.rs | 7 ++++ .../noirc_evaluator/src/ssa/parser/tests.rs | 15 ++++++++ .../noirc_evaluator/src/ssa/parser/token.rs | 6 +++ 5 files changed, 66 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index b71025c45e6..bd377af4b1b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -176,6 +176,10 @@ impl<'a> Parser<'a> { return Ok(terminator); } + if let Some(terminator) = self.parse_jmpif()? { + return Ok(terminator); + } + self.expected_instruction_or_terminator() } @@ -201,6 +205,23 @@ impl<'a> Parser<'a> { Ok(Some(ParsedTerminator::Jmp { destination, arguments })) } + fn parse_jmpif(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::Jmpif)? { + return Ok(None); + } + + let condition = self.parse_value_or_error()?; + self.eat_or_error(Token::Keyword(Keyword::Then))?; + self.eat_or_error(Token::Colon)?; + let then_block = self.parse_identifier_or_error()?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::Else))?; + self.eat_or_error(Token::Colon)?; + let else_block = self.parse_identifier_or_error()?; + + Ok(Some(ParsedTerminator::Jmpif { condition, then_block, else_block })) + } + fn parse_comma_separated_values(&mut self) -> ParseResult> { let mut values = Vec::new(); while let Some(value) = self.parse_value()? { @@ -212,6 +233,14 @@ impl<'a> Parser<'a> { Ok(values) } + fn parse_value_or_error(&mut self) -> ParseResult { + if let Some(value) = self.parse_value()? { + Ok(value) + } else { + self.expected_value() + } + } + fn parse_value(&mut self) -> ParseResult> { if let Some(value) = self.parse_field_value()? { return Ok(Some(value)); @@ -420,6 +449,13 @@ impl<'a> Parser<'a> { }) } + fn expected_value(&mut self) -> ParseResult { + Err(ParserError::ExpectedValue { + found: self.token.token().clone(), + span: self.token.to_span(), + }) + } + fn expected_token(&mut self, token: Token) -> ParseResult { Err(ParserError::ExpectedToken { token, @@ -446,6 +482,7 @@ pub(crate) enum ParserError { ExpectedInt { found: Token, span: Span }, ExpectedType { found: Token, span: Span }, ExpectedInstructionOrTerminator { found: Token, span: Span }, + ExpectedValue { found: Token, span: Span }, } fn eof_spanned_token() -> SpannedToken { diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 168d859d518..648f4ce1051 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -48,6 +48,7 @@ pub(crate) enum ParsedInstruction {} #[derive(Debug)] pub(crate) enum ParsedTerminator { Jmp { destination: Identifier, arguments: Vec }, + Jmpif { condition: ParsedValue, then_block: Identifier, else_block: Identifier }, Return(Vec), } diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index fb465f9f7dd..ca01b762fc2 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -81,6 +81,13 @@ impl Translator { self.builder.terminate_with_jmp(block_id, translated_arguments); } + ParsedTerminator::Jmpif { condition, then_block, else_block } => { + let condition = self.translate_value(condition)?; + let then_destination = self.lookup_block(then_block)?; + let else_destination = self.lookup_block(else_block)?; + + self.builder.terminate_with_jmpif(condition, then_destination, else_destination); + } ParsedTerminator::Return(values) => { let mut return_values = Vec::with_capacity(values.len()); for value in values { diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 1377135021f..12aaff56326 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -106,3 +106,18 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_jmpif() { + let src: &str = " +acir(inline) fn main f0 { + b0(v0: Field): + jmpif v0 then: b1, else: b2 + b1(): + return v0 + b2(): + return v0 +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 5274cee8bee..c4e9f91fab4 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -64,13 +64,16 @@ pub(crate) enum Keyword { Brillig, Inline, InlineAlways, + Else, Field, Fold, Fn, Jmp, + Jmpif, NoPredicates, Of, Return, + Then, } impl Keyword { @@ -78,15 +81,18 @@ impl Keyword { let keyword = match word { "acir" => Keyword::Acir, "brillig" => Keyword::Brillig, + "else" => Keyword::Else, "inline" => Keyword::Inline, "inline_always" => Keyword::InlineAlways, "Field" => Keyword::Field, "fold" => Keyword::Fold, "fn" => Keyword::Fn, "jmp" => Keyword::Jmp, + "jmpif" => Keyword::Jmpif, "no_predicates" => Keyword::NoPredicates, "of" => Keyword::Of, "return" => Keyword::Return, + "then" => Keyword::Then, _ => return None, }; Some(Token::Keyword(keyword)) From 3861d82679bb3e1dde1086369714a5793d5331c3 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 11:43:54 -0300 Subject: [PATCH 12/65] Handle ambiguity with return --- compiler/noirc_evaluator/src/ssa/parser.rs | 20 ++++++++++++++++++- .../noirc_evaluator/src/ssa/parser/lexer.rs | 7 ++++++- .../noirc_evaluator/src/ssa/parser/tests.rs | 4 ++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index bd377af4b1b..cd8344eeeb7 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -184,11 +184,25 @@ impl<'a> Parser<'a> { } fn parse_return(&mut self) -> ParseResult> { + // Before advancing to the next token (after a potential return keyword), + // we check if a newline follows. This is because if we have this: + // + // return + // b1(): + // ... + // + // then unless we check for a newline we can't know if the return + // returns `b1` or not (we could check if a parentheses comes next, but + // that would require a look-ahead and, for the purpose of the SSA parser, + // it's just simpler to check if a newline follows) + let newline_follows = self.newline_follows(); + if !self.eat_keyword(Keyword::Return)? { return Ok(None); } - let values = self.parse_comma_separated_values()?; + let values = + if newline_follows { Vec::new() } else { self.parse_comma_separated_values()? }; Ok(Some(ParsedTerminator::Return(values))) } @@ -412,6 +426,10 @@ impl<'a> Parser<'a> { self.at(Token::Keyword(keyword)) } + fn newline_follows(&self) -> bool { + self.lexer.newline_follows() + } + fn bump(&mut self) -> ParseResult { let token = self.read_token_internal()?; Ok(std::mem::replace(&mut self.token, token)) diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index 9684d577428..9969f2038b8 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -202,13 +202,18 @@ impl<'a> Lexer<'a> { Some(ch) } - fn peek_char(&mut self) -> Option { + fn peek_char(&self) -> Option { self.chars.clone().next().map(|(_, ch)| ch) } fn is_code_whitespace(c: char) -> bool { c.is_ascii_whitespace() } + + pub(crate) fn newline_follows(&self) -> bool { + let chars = self.chars.clone(); + chars.take_while(|(_, char)| char.is_ascii_whitespace()).any(|(_, char)| char == '\n') + } } type SpannedTokenResult = Result; diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 12aaff56326..e27909a8bce 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -114,9 +114,9 @@ acir(inline) fn main f0 { b0(v0: Field): jmpif v0 then: b1, else: b2 b1(): - return v0 + return b2(): - return v0 + return } "; assert_ssa_roundtrip(src); From cead10535c5fcd5f3ea03e439efd5d3577710564 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 13:50:01 -0300 Subject: [PATCH 13/65] Functions and calls --- .../noirc_evaluator/src/ssa/ir/printer.rs | 16 +- compiler/noirc_evaluator/src/ssa/ir/types.rs | 6 +- compiler/noirc_evaluator/src/ssa/parser.rs | 121 +++++++++++---- .../noirc_evaluator/src/ssa/parser/ast.rs | 5 +- .../src/ssa/parser/into_ssa.rs | 145 ++++++++++++++---- .../noirc_evaluator/src/ssa/parser/lexer.rs | 9 ++ .../noirc_evaluator/src/ssa/parser/tests.rs | 28 +++- .../noirc_evaluator/src/ssa/parser/token.rs | 8 + 8 files changed, 265 insertions(+), 73 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 464aa8ec540..e930ef87e1e 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -18,7 +18,21 @@ use super::{ /// Helper function for Function's Display impl to pretty-print the function with the given formatter. pub(crate) fn display_function(function: &Function, f: &mut Formatter) -> Result { - writeln!(f, "{} fn {} {} {{", function.runtime(), function.name(), function.id())?; + write!(f, "{} fn {} {}", function.runtime(), function.name(), function.id())?; + + let return_value_ids = function.returns(); + if !return_value_ids.is_empty() { + let return_types = vecmap(return_value_ids, |id| function.dfg.type_of_value(*id)); + let return_types_str = + return_types.iter().map(|typ| typ.to_string()).collect::>().join(", "); + if return_types.len() == 1 { + write!(f, " -> {}", return_types_str)?; + } else { + write!(f, " -> ({})", return_types_str)?; + } + } + + writeln!(f, " {{")?; display_block_with_successors(function, function.entry_block(), &mut HashSet::new(), f)?; write!(f, "}}") } diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index b7ee37ba17a..203356de8e6 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -218,7 +218,11 @@ impl std::fmt::Display for Type { Type::Reference(element) => write!(f, "&mut {element}"), Type::Array(element, length) => { let elements = vecmap(element.iter(), |element| element.to_string()); - write!(f, "[{}; {length}]", elements.join(", ")) + if elements.len() == 1 { + write!(f, "[{}; {length}]", elements.join(", ")) + } else { + write!(f, "[({}); {length}]", elements.join(", ")) + } } Type::Slice(element) => { let elements = vecmap(element.iter(), |element| element.to_string()); diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index cd8344eeeb7..17e7ad698b6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -2,8 +2,11 @@ use std::sync::Arc; use super::{ir::types::Type, Ssa}; -use acvm::FieldElement; -use ast::{Identifier, ParsedBlock, ParsedFunction, ParsedParameter, ParsedSsa, ParsedValue}; +use acvm::{AcirField, FieldElement}; +use ast::{ + Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedParameter, ParsedSsa, + ParsedValue, +}; use lexer::{Lexer, LexerError}; use noirc_errors::Span; use noirc_frontend::{monomorphization::ast::InlineType, token::IntType}; @@ -30,6 +33,7 @@ pub(crate) enum SsaError { ParserError(ParserError), UnknownVariable(Identifier), UnknownBlock(Identifier), + UnknownFunction(Identifier), } type ParseResult = Result; @@ -63,13 +67,15 @@ impl<'a> Parser<'a> { let external_name = self.eat_ident_or_error()?; let internal_name = self.eat_ident_or_error()?; + let return_types = if self.eat(Token::Arrow)? { self.parse_types()? } else { Vec::new() }; + self.eat_or_error(Token::LeftBrace)?; let blocks = self.parse_blocks()?; self.eat_or_error(Token::RightBrace)?; - Ok(ParsedFunction { runtime_type, external_name, internal_name, blocks }) + Ok(ParsedFunction { runtime_type, external_name, internal_name, return_types, blocks }) } fn parse_runtime_type(&mut self) -> ParseResult { @@ -138,33 +144,39 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::RightParen)?; self.eat_or_error(Token::Colon)?; - let instructions = Vec::new(); + let instructions = self.parse_instructions()?; let terminator = self.parse_terminator()?; Ok(ParsedBlock { name, parameters, instructions, terminator }) } fn parse_parameter(&mut self) -> ParseResult { - let identifier = self.parse_identifier_or_error()?; + let identifier = self.eat_identifier_or_error()?; self.eat_or_error(Token::Colon)?; let typ = self.parse_type()?; Ok(ParsedParameter { identifier, typ }) } - fn parse_identifier_or_error(&mut self) -> ParseResult { - if let Some(identifier) = self.parse_identifier()? { - Ok(identifier) - } else { - self.expected_identifier() + fn parse_instructions(&mut self) -> ParseResult> { + let mut instructions = Vec::new(); + while let Some(instruction) = self.parse_instruction()? { + instructions.push(instruction); } + Ok(instructions) } - fn parse_identifier(&mut self) -> ParseResult> { - let span = self.token.to_span(); - if let Some(name) = self.eat_ident()? { - Ok(Some(Identifier::new(name, span))) - } else { - Ok(None) + fn parse_instruction(&mut self) -> ParseResult> { + if let Some(lvalue) = self.eat_identifier()? { + self.eat_or_error(Token::Assign)?; + if self.eat_keyword(Keyword::Call)? { + let function = self.eat_identifier_or_error()?; + let arguments = self.parse_arguments()?; + return Ok(Some(ParsedInstruction::Call { lvalue, function, arguments })); + } else { + return self.expected_instruction_or_terminator(); + } } + + Ok(None) } fn parse_terminator(&mut self) -> ParseResult { @@ -211,11 +223,8 @@ impl<'a> Parser<'a> { return Ok(None); } - let destination = self.parse_identifier_or_error()?; - self.eat_or_error(Token::LeftParen)?; - let arguments = self.parse_comma_separated_values()?; - self.eat_or_error(Token::RightParen)?; - + let destination = self.eat_identifier_or_error()?; + let arguments = self.parse_arguments()?; Ok(Some(ParsedTerminator::Jmp { destination, arguments })) } @@ -227,15 +236,22 @@ impl<'a> Parser<'a> { let condition = self.parse_value_or_error()?; self.eat_or_error(Token::Keyword(Keyword::Then))?; self.eat_or_error(Token::Colon)?; - let then_block = self.parse_identifier_or_error()?; + let then_block = self.eat_identifier_or_error()?; self.eat_or_error(Token::Comma)?; self.eat_or_error(Token::Keyword(Keyword::Else))?; self.eat_or_error(Token::Colon)?; - let else_block = self.parse_identifier_or_error()?; + let else_block = self.eat_identifier_or_error()?; Ok(Some(ParsedTerminator::Jmpif { condition, then_block, else_block })) } + fn parse_arguments(&mut self) -> ParseResult> { + self.eat_or_error(Token::LeftParen)?; + let arguments = self.parse_comma_separated_values()?; + self.eat_or_error(Token::RightParen)?; + Ok(arguments) + } + fn parse_comma_separated_values(&mut self) -> ParseResult> { let mut values = Vec::new(); while let Some(value) = self.parse_value()? { @@ -268,7 +284,7 @@ impl<'a> Parser<'a> { return Ok(Some(value)); } - if let Some(identifier) = self.parse_identifier()? { + if let Some(identifier) = self.eat_identifier()? { return Ok(Some(ParsedValue::Variable(identifier))); } @@ -302,19 +318,28 @@ impl<'a> Parser<'a> { let values = self.parse_comma_separated_values()?; self.eat_or_error(Token::RightBracket)?; self.eat_or_error(Token::Keyword(Keyword::Of))?; - let types = if self.eat(Token::LeftParen)? { - let types = self.parse_comma_separated_types()?; - self.eat_or_error(Token::RightParen)?; - types - } else { - vec![self.parse_type()?] - }; - Ok(Some(ParsedValue::Array { typ: Type::Array(Arc::new(types), values.len()), values })) + let types = self.parse_types()?; + let types_len = types.len(); + let values_len = values.len(); + Ok(Some(ParsedValue::Array { + typ: Type::Array(Arc::new(types), values_len / types_len), + values, + })) } else { Ok(None) } } + fn parse_types(&mut self) -> ParseResult> { + if self.eat(Token::LeftParen)? { + let types = self.parse_comma_separated_types()?; + self.eat_or_error(Token::RightParen)?; + Ok(types) + } else { + Ok(vec![self.parse_type()?]) + } + } + fn parse_comma_separated_types(&mut self) -> ParseResult> { let mut types = Vec::new(); loop { @@ -332,9 +357,41 @@ impl<'a> Parser<'a> { return Ok(Type::field()); } + if let Some(int_type) = self.eat_int_type()? { + return Ok(match int_type { + IntType::Unsigned(bit_size) => Type::unsigned(bit_size), + IntType::Signed(bit_size) => Type::signed(bit_size), + }); + } + + if self.eat(Token::LeftBracket)? { + let element_types = self.parse_types()?; + self.eat_or_error(Token::Semicolon)?; + let length = self.eat_int_or_error()?; + self.eat_or_error(Token::RightBracket)?; + return Ok(Type::Array(Arc::new(element_types), length.to_u128() as usize)); + } + self.expected_type() } + fn eat_identifier_or_error(&mut self) -> ParseResult { + if let Some(identifier) = self.eat_identifier()? { + Ok(identifier) + } else { + self.expected_identifier() + } + } + + fn eat_identifier(&mut self) -> ParseResult> { + let span = self.token.to_span(); + if let Some(name) = self.eat_ident()? { + Ok(Some(Identifier::new(name, span))) + } else { + Ok(None) + } + } + fn eat_keyword(&mut self, keyword: Keyword) -> ParseResult { if let Token::Keyword(kw) = self.token.token() { if *kw == keyword { diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 648f4ce1051..c1185bca7f5 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -13,6 +13,7 @@ pub(crate) struct ParsedFunction { pub(crate) runtime_type: RuntimeType, pub(crate) external_name: String, pub(crate) internal_name: String, + pub(crate) return_types: Vec, pub(crate) blocks: Vec, } @@ -43,7 +44,9 @@ impl Identifier { } #[derive(Debug)] -pub(crate) enum ParsedInstruction {} +pub(crate) enum ParsedInstruction { + Call { lvalue: Identifier, function: Identifier, arguments: Vec }, +} #[derive(Debug)] pub(crate) enum ParsedTerminator { diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index ca01b762fc2..e780cbbce97 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -8,13 +8,18 @@ use crate::ssa::{ }; use super::{ - Identifier, ParsedBlock, ParsedFunction, ParsedSsa, ParsedTerminator, ParsedValue, Ssa, - SsaError, + Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedSsa, ParsedTerminator, + ParsedValue, RuntimeType, Ssa, SsaError, Type, }; impl ParsedSsa { pub(crate) fn into_ssa(mut self) -> Result { - let translator = Translator::new(&mut self)?; + let mut translator = Translator::new(&mut self)?; + + for function in self.functions { + translator.translate_function(function)?; + } + Ok(translator.finish()) } } @@ -22,11 +27,14 @@ impl ParsedSsa { struct Translator { builder: FunctionBuilder, + /// Maps function names to their ID and types + functions: HashMap)>, + /// Maps block names to their IDs - blocks: HashMap, + blocks: HashMap>, - /// Maps parameter names to their IDs - parameters: HashMap, + /// Maps variable names to their IDs + variables: HashMap>, } impl Translator { @@ -36,23 +44,54 @@ impl Translator { let mut builder = FunctionBuilder::new(main_function.external_name.clone(), main_id); builder.set_runtime(main_function.runtime_type); - let mut translator = Self { builder, parameters: HashMap::new(), blocks: HashMap::new() }; + // Map function names to their IDs so calls can be resolved + let mut function_id_counter = 1; + let mut functions = HashMap::new(); + for function in &parsed_ssa.functions { + let function_id = FunctionId::new(function_id_counter); + function_id_counter += 1; + + functions.insert( + function.internal_name.clone(), + (function_id, function.return_types.clone()), + ); + } + + let mut translator = + Self { builder, functions, variables: HashMap::new(), blocks: HashMap::new() }; translator.translate_function_body(main_function)?; + Ok(translator) } - fn translate_function_body(&mut self, mut function: ParsedFunction) -> Result<(), SsaError> { - // First define all blocks so that they are known (a block might jump to a block that comes next) - let entry_block = function.blocks.remove(0); - let entry_block_id = self.builder.current_function.entry_block(); - self.blocks.insert(entry_block.name.clone(), entry_block_id); + fn translate_function(&mut self, function: ParsedFunction) -> Result<(), SsaError> { + let (function_id, _) = self.functions[&function.internal_name]; + let external_name = function.external_name.clone(); - for block in &function.blocks { - let block_id = self.builder.insert_block(); - self.blocks.insert(block.name.clone(), block_id); + match function.runtime_type { + RuntimeType::Acir(inline_type) => { + self.builder.new_function(external_name, function_id, inline_type); + } + RuntimeType::Brillig(inline_type) => { + self.builder.new_brillig_function(external_name, function_id, inline_type); + } } - self.translate_block(entry_block)?; + self.translate_function_body(function) + } + + fn translate_function_body(&mut self, function: ParsedFunction) -> Result<(), SsaError> { + // First define all blocks so that they are known (a block might jump to a block that comes next) + for (index, block) in function.blocks.iter().enumerate() { + // The first block is the entry block and it was automatically created by the builder + let block_id = if index == 0 { + self.builder.current_function.entry_block() + } else { + self.builder.insert_block() + }; + let entry = self.blocks.entry(self.current_function_id()).or_default(); + entry.insert(block.name.clone(), block_id); + } for block in function.blocks { self.translate_block(block)?; @@ -62,37 +101,33 @@ impl Translator { } fn translate_block(&mut self, block: ParsedBlock) -> Result<(), SsaError> { - let block_id = self.blocks[&block.name]; + let block_id = self.blocks[&self.current_function_id()][&block.name]; self.builder.switch_to_block(block_id); for parameter in block.parameters { let parameter_value_id = self.builder.add_block_parameter(block_id, parameter.typ); - self.parameters.insert(parameter.identifier.name, parameter_value_id); + let entry = self.variables.entry(self.current_function_id()).or_default(); + entry.insert(parameter.identifier.name, parameter_value_id); + } + + for instruction in block.instructions { + self.translate_instruction(instruction)?; } match block.terminator { ParsedTerminator::Jmp { destination, arguments } => { let block_id = self.lookup_block(destination)?; - - let mut translated_arguments = Vec::with_capacity(arguments.len()); - for value in arguments { - translated_arguments.push(self.translate_value(value)?); - } - - self.builder.terminate_with_jmp(block_id, translated_arguments); + let arguments = self.translate_values(arguments)?; + self.builder.terminate_with_jmp(block_id, arguments); } ParsedTerminator::Jmpif { condition, then_block, else_block } => { let condition = self.translate_value(condition)?; let then_destination = self.lookup_block(then_block)?; let else_destination = self.lookup_block(else_block)?; - self.builder.terminate_with_jmpif(condition, then_destination, else_destination); } ParsedTerminator::Return(values) => { - let mut return_values = Vec::with_capacity(values.len()); - for value in values { - return_values.push(self.translate_value(value)?); - } + let return_values = self.translate_values(values)?; self.builder.terminate_with_return(return_values); } } @@ -100,6 +135,37 @@ impl Translator { Ok(()) } + fn translate_instruction(&mut self, instruction: ParsedInstruction) -> Result<(), SsaError> { + match instruction { + ParsedInstruction::Call { lvalue, function, arguments } => { + let (function_id, return_types) = self.lookup_function(function)?; + let result_types = return_types.to_vec(); + + let function_id = self.builder.import_function(function_id); + let arguments = self.translate_values(arguments)?; + + let current_function_id = self.current_function_id(); + + // TODO: support multiple values + let value_ids = self.builder.insert_call(function_id, arguments, result_types); + assert_eq!(value_ids.len(), 1); + + let entry = self.variables.entry(current_function_id).or_default(); + entry.insert(lvalue.name, value_ids[0]); + } + } + + Ok(()) + } + + fn translate_values(&mut self, values: Vec) -> Result, SsaError> { + let mut translated_values = Vec::with_capacity(values.len()); + for value in values { + translated_values.push(self.translate_value(value)?); + } + Ok(translated_values) + } + fn translate_value(&mut self, value: ParsedValue) -> Result { match value { ParsedValue::NumericConstant { constant, typ } => { @@ -117,7 +183,7 @@ impl Translator { } fn lookup_variable(&mut self, identifier: Identifier) -> Result { - if let Some(value_id) = self.parameters.get(&identifier.name) { + if let Some(value_id) = self.variables[&self.current_function_id()].get(&identifier.name) { Ok(*value_id) } else { Err(SsaError::UnknownVariable(identifier)) @@ -125,14 +191,29 @@ impl Translator { } fn lookup_block(&mut self, identifier: Identifier) -> Result { - if let Some(block_id) = self.blocks.get(&identifier.name) { + if let Some(block_id) = self.blocks[&self.current_function_id()].get(&identifier.name) { Ok(*block_id) } else { Err(SsaError::UnknownBlock(identifier)) } } + fn lookup_function( + &mut self, + identifier: Identifier, + ) -> Result<(FunctionId, &[Type]), SsaError> { + if let Some((function_id, types)) = self.functions.get(&identifier.name) { + Ok((*function_id, types)) + } else { + Err(SsaError::UnknownFunction(identifier)) + } + } + fn finish(self) -> Ssa { self.builder.finish() } + + fn current_function_id(&self) -> FunctionId { + self.builder.current_function.id() + } } diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index 9969f2038b8..38514ace812 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -38,14 +38,17 @@ impl<'a> Lexer<'a> { } self.next_token() } + Some('=') => self.single_char_token(Token::Assign), Some(',') => self.single_char_token(Token::Comma), Some(':') => self.single_char_token(Token::Colon), + Some(';') => self.single_char_token(Token::Semicolon), Some('(') => self.single_char_token(Token::LeftParen), Some(')') => self.single_char_token(Token::RightParen), Some('{') => self.single_char_token(Token::LeftBrace), Some('}') => self.single_char_token(Token::RightBrace), Some('[') => self.single_char_token(Token::LeftBracket), Some(']') => self.single_char_token(Token::RightBracket), + Some('-') if self.peek_char() == Some('>') => self.double_char_token(Token::Arrow), Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch), Some(char) => Err(LexerError::UnexpectedCharacter { char, @@ -196,6 +199,12 @@ impl<'a> Lexer<'a> { Ok(token.into_single_span(self.position)) } + fn double_char_token(&mut self, token: Token) -> SpannedTokenResult { + let start_position = self.position; + self.next_char(); + Ok(token.into_span(start_position, self.position)) + } + fn next_char(&mut self) -> Option { let (position, ch) = self.chars.next()?; self.position = position as u32; diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index e27909a8bce..3e9457c244f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -40,7 +40,7 @@ fn test_return_integer() { for typ in ["u1", "u8", "u16", "u32", "u64", "i1", "i8", "i16", "i32", "i64", "Field"] { let src = format!( " -acir(inline) fn main f0 {{ +acir(inline) fn main f0 -> {typ} {{ b0(): return {typ} 1 }} @@ -53,7 +53,7 @@ acir(inline) fn main f0 {{ #[test] fn test_return_array() { let src = " -acir(inline) fn main f0 { +acir(inline) fn main f0 -> [Field; 1] { b0(): return [Field 1] of Field } @@ -64,7 +64,7 @@ acir(inline) fn main f0 { #[test] fn test_return_empty_array() { let src = " -acir(inline) fn main f0 { +acir(inline) fn main f0 -> [Field; 0] { b0(): return [] of Field } @@ -75,7 +75,7 @@ acir(inline) fn main f0 { #[test] fn test_return_composite_array() { let src = " -acir(inline) fn main f0 { +acir(inline) fn main f0 -> [(Field, Field); 1] { b0(): return [Field 1, Field 2] of (Field, Field) } @@ -86,7 +86,7 @@ acir(inline) fn main f0 { #[test] fn test_block_parameters() { let src = " -acir(inline) fn main f0 { +acir(inline) fn main f0 -> (Field, Field) { b0(v0: Field, v1: Field): return v0, v1 } @@ -97,7 +97,7 @@ acir(inline) fn main f0 { #[test] fn test_multiple_blocks_and_jmp() { let src: &str = " -acir(inline) fn main f0 { +acir(inline) fn main f0 -> Field { b0(): jmp b1(Field 1) b1(v1: Field): @@ -121,3 +121,19 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_call() { + let src: &str = " +acir(inline) fn main f0 -> Field { + b0(v0: Field): + v2 = call f1(v0) + return v2 +} +acir(inline) fn foo f1 -> Field { + b0(v0: Field): + return v0 +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index c4e9f91fab4..674b4098368 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -29,6 +29,8 @@ pub(crate) enum Token { Int(FieldElement), Keyword(Keyword), IntType(IntType), + /// = + Assign, /// ( LeftParen, /// ) @@ -45,6 +47,10 @@ pub(crate) enum Token { Comma, /// : Colon, + /// ; + Semicolon, + /// -> + Arrow, Eof, } @@ -62,6 +68,7 @@ impl Token { pub(crate) enum Keyword { Acir, Brillig, + Call, Inline, InlineAlways, Else, @@ -81,6 +88,7 @@ impl Keyword { let keyword = match word { "acir" => Keyword::Acir, "brillig" => Keyword::Brillig, + "call" => Keyword::Call, "else" => Keyword::Else, "inline" => Keyword::Inline, "inline_always" => Keyword::InlineAlways, From 374497453ccb511ae7e114c7b0ee2ebf6eeb06e8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 14:02:24 -0300 Subject: [PATCH 14/65] Multiple return values --- compiler/noirc_evaluator/src/ssa/parser.rs | 12 ++++++++++-- compiler/noirc_evaluator/src/ssa/parser/ast.rs | 2 +- .../noirc_evaluator/src/ssa/parser/into_ssa.rs | 18 ++++++++++++------ .../noirc_evaluator/src/ssa/parser/tests.rs | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 17e7ad698b6..f62bdc424f7 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -34,6 +34,7 @@ pub(crate) enum SsaError { UnknownVariable(Identifier), UnknownBlock(Identifier), UnknownFunction(Identifier), + MismatchedReturnValues { returns: Vec, expected: usize }, } type ParseResult = Result; @@ -165,12 +166,19 @@ impl<'a> Parser<'a> { } fn parse_instruction(&mut self) -> ParseResult> { - if let Some(lvalue) = self.eat_identifier()? { + if let Some(target) = self.eat_identifier()? { + let mut targets = vec![target]; + + while self.eat(Token::Comma)? { + let target = self.eat_identifier_or_error()?; + targets.push(target); + } + self.eat_or_error(Token::Assign)?; if self.eat_keyword(Keyword::Call)? { let function = self.eat_identifier_or_error()?; let arguments = self.parse_arguments()?; - return Ok(Some(ParsedInstruction::Call { lvalue, function, arguments })); + return Ok(Some(ParsedInstruction::Call { targets, function, arguments })); } else { return self.expected_instruction_or_terminator(); } diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index c1185bca7f5..9b23ea5ed39 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -45,7 +45,7 @@ impl Identifier { #[derive(Debug)] pub(crate) enum ParsedInstruction { - Call { lvalue: Identifier, function: Identifier, arguments: Vec }, + Call { targets: Vec, function: Identifier, arguments: Vec }, } #[derive(Debug)] diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index e780cbbce97..9b309fe1077 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -137,7 +137,7 @@ impl Translator { fn translate_instruction(&mut self, instruction: ParsedInstruction) -> Result<(), SsaError> { match instruction { - ParsedInstruction::Call { lvalue, function, arguments } => { + ParsedInstruction::Call { targets, function, arguments } => { let (function_id, return_types) = self.lookup_function(function)?; let result_types = return_types.to_vec(); @@ -145,13 +145,19 @@ impl Translator { let arguments = self.translate_values(arguments)?; let current_function_id = self.current_function_id(); - - // TODO: support multiple values let value_ids = self.builder.insert_call(function_id, arguments, result_types); - assert_eq!(value_ids.len(), 1); - let entry = self.variables.entry(current_function_id).or_default(); - entry.insert(lvalue.name, value_ids[0]); + if value_ids.len() != targets.len() { + return Err(SsaError::MismatchedReturnValues { + returns: targets, + expected: value_ids.len(), + }); + } + + for (target, value_id) in targets.into_iter().zip(value_ids.iter()) { + let entry = self.variables.entry(current_function_id).or_default(); + entry.insert(target.name, *value_id); + } } } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 3e9457c244f..bb6778e154a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -137,3 +137,19 @@ acir(inline) fn foo f1 -> Field { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_call_multiple_return_values() { + let src: &str = " +acir(inline) fn main f0 -> [Field; 3] { + b0(): + v1, v2 = call f1() + return v1 +} +acir(inline) fn foo f1 -> ([Field; 3], [Field; 1]) { + b0(): + return [Field 1, Field 2, Field 3] of Field, [Field 4] of Field +} +"; + assert_ssa_roundtrip(src); +} From 1d7c2a220ae35b9454da2da36bba4e518766d8fa Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 14:15:48 -0300 Subject: [PATCH 15/65] cast --- compiler/noirc_evaluator/src/ssa/parser.rs | 21 +++++++++++++++++-- .../noirc_evaluator/src/ssa/parser/ast.rs | 3 ++- .../src/ssa/parser/into_ssa.rs | 6 ++++++ .../noirc_evaluator/src/ssa/parser/tests.rs | 12 +++++++++++ .../noirc_evaluator/src/ssa/parser/token.rs | 4 ++++ 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index f62bdc424f7..622f49f45c7 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -175,13 +175,29 @@ impl<'a> Parser<'a> { } self.eat_or_error(Token::Assign)?; + if self.eat_keyword(Keyword::Call)? { let function = self.eat_identifier_or_error()?; let arguments = self.parse_arguments()?; return Ok(Some(ParsedInstruction::Call { targets, function, arguments })); - } else { - return self.expected_instruction_or_terminator(); } + + if targets.len() > 1 { + return Err(ParserError::MultipleReturnValuesOnlyAllowedForCall { + second_target: targets[1].clone(), + }); + } + + let target = targets.remove(0); + + if self.eat_keyword(Keyword::Cast)? { + let lhs = self.parse_value_or_error()?; + self.eat_or_error(Token::Keyword(Keyword::As))?; + let typ = self.parse_type()?; + return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); + } + + return self.expected_instruction_or_terminator(); } Ok(None) @@ -566,6 +582,7 @@ pub(crate) enum ParserError { ExpectedType { found: Token, span: Span }, ExpectedInstructionOrTerminator { found: Token, span: Span }, ExpectedValue { found: Token, span: Span }, + MultipleReturnValuesOnlyAllowedForCall { second_target: Identifier }, } fn eof_spanned_token() -> SpannedToken { diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 9b23ea5ed39..ae2986a3275 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -31,7 +31,7 @@ pub(crate) struct ParsedParameter { pub(crate) typ: Type, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct Identifier { pub(crate) name: String, pub(crate) span: Span, @@ -46,6 +46,7 @@ impl Identifier { #[derive(Debug)] pub(crate) enum ParsedInstruction { Call { targets: Vec, function: Identifier, arguments: Vec }, + Cast { target: Identifier, lhs: ParsedValue, typ: Type }, } #[derive(Debug)] diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 9b309fe1077..e0f36c2f09f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -159,6 +159,12 @@ impl Translator { entry.insert(target.name, *value_id); } } + ParsedInstruction::Cast { target, lhs, typ } => { + let lhs = self.translate_value(lhs)?; + let value_id = self.builder.insert_cast(lhs, typ); + let entry = self.variables.entry(self.current_function_id()).or_default(); + entry.insert(target.name, value_id); + } } Ok(()) diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index bb6778e154a..2544390520c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -153,3 +153,15 @@ acir(inline) fn foo f1 -> ([Field; 3], [Field; 1]) { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_cast() { + let src: &str = " +acir(inline) fn main f0 -> i32 { + b0(v0: Field): + v1 = cast v0 as i32 + return v1 +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 674b4098368..3ce70b7e11b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -67,8 +67,10 @@ impl Token { #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Keyword { Acir, + As, Brillig, Call, + Cast, Inline, InlineAlways, Else, @@ -87,8 +89,10 @@ impl Keyword { pub(crate) fn lookup_keyword(word: &str) -> Option { let keyword = match word { "acir" => Keyword::Acir, + "as" => Keyword::As, "brillig" => Keyword::Brillig, "call" => Keyword::Call, + "cast" => Keyword::Cast, "else" => Keyword::Else, "inline" => Keyword::Inline, "inline_always" => Keyword::InlineAlways, From af954ece5a62e46bdfca661de2cf2bd95d937208 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 14:23:01 -0300 Subject: [PATCH 16/65] Helper to define variables --- compiler/noirc_evaluator/src/ssa/parser.rs | 1 + .../src/ssa/parser/into_ssa.rs | 32 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 622f49f45c7..46784794dfc 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -35,6 +35,7 @@ pub(crate) enum SsaError { UnknownBlock(Identifier), UnknownFunction(Identifier), MismatchedReturnValues { returns: Vec, expected: usize }, + VariableAlreadyDefined(Identifier), } type ParseResult = Result; diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index e0f36c2f09f..85df24f78fc 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -106,8 +106,7 @@ impl Translator { for parameter in block.parameters { let parameter_value_id = self.builder.add_block_parameter(block_id, parameter.typ); - let entry = self.variables.entry(self.current_function_id()).or_default(); - entry.insert(parameter.identifier.name, parameter_value_id); + self.define_variable(parameter.identifier, parameter_value_id)?; } for instruction in block.instructions { @@ -144,8 +143,8 @@ impl Translator { let function_id = self.builder.import_function(function_id); let arguments = self.translate_values(arguments)?; - let current_function_id = self.current_function_id(); - let value_ids = self.builder.insert_call(function_id, arguments, result_types); + let value_ids = + self.builder.insert_call(function_id, arguments, result_types).to_vec(); if value_ids.len() != targets.len() { return Err(SsaError::MismatchedReturnValues { @@ -154,16 +153,14 @@ impl Translator { }); } - for (target, value_id) in targets.into_iter().zip(value_ids.iter()) { - let entry = self.variables.entry(current_function_id).or_default(); - entry.insert(target.name, *value_id); + for (target, value_id) in targets.into_iter().zip(value_ids.into_iter()) { + self.define_variable(target, value_id)?; } } ParsedInstruction::Cast { target, lhs, typ } => { let lhs = self.translate_value(lhs)?; let value_id = self.builder.insert_cast(lhs, typ); - let entry = self.variables.entry(self.current_function_id()).or_default(); - entry.insert(target.name, value_id); + self.define_variable(target, value_id)?; } } @@ -194,6 +191,23 @@ impl Translator { } } + fn define_variable( + &mut self, + identifier: Identifier, + value_id: ValueId, + ) -> Result<(), SsaError> { + if let Some(vars) = self.variables.get(&self.current_function_id()) { + if vars.contains_key(&identifier.name) { + return Err(SsaError::VariableAlreadyDefined(identifier)); + } + } + + let entry = self.variables.entry(self.current_function_id()).or_default(); + entry.insert(identifier.name, value_id); + + Ok(()) + } + fn lookup_variable(&mut self, identifier: Identifier) -> Result { if let Some(value_id) = self.variables[&self.current_function_id()].get(&identifier.name) { Ok(*value_id) From e179d10baba7e8b15db15a9a59777956f063bfb4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 14:30:20 -0300 Subject: [PATCH 17/65] constrain --- compiler/noirc_evaluator/src/ssa/parser.rs | 7 +++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 1 + compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 5 +++++ compiler/noirc_evaluator/src/ssa/parser/lexer.rs | 1 + compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 4 ++++ 6 files changed, 30 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 46784794dfc..83ac0c38f36 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -167,6 +167,13 @@ impl<'a> Parser<'a> { } fn parse_instruction(&mut self) -> ParseResult> { + if self.eat_keyword(Keyword::Constrain)? { + let lhs = self.parse_value_or_error()?; + self.eat_or_error(Token::Equal)?; + let rhs = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::Constrain { lhs, rhs })); + } + if let Some(target) = self.eat_identifier()? { let mut targets = vec![target]; diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index ae2986a3275..b1b2bf95173 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -47,6 +47,7 @@ impl Identifier { pub(crate) enum ParsedInstruction { Call { targets: Vec, function: Identifier, arguments: Vec }, Cast { target: Identifier, lhs: ParsedValue, typ: Type }, + Constrain { lhs: ParsedValue, rhs: ParsedValue }, } #[derive(Debug)] diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 85df24f78fc..8044171391b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -162,6 +162,11 @@ impl Translator { let value_id = self.builder.insert_cast(lhs, typ); self.define_variable(target, value_id)?; } + ParsedInstruction::Constrain { lhs, rhs } => { + let lhs = self.translate_value(lhs)?; + let rhs = self.translate_value(rhs)?; + self.builder.insert_constrain(lhs, rhs, None); + } } Ok(()) diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index 38514ace812..a4e9941f3fe 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -38,6 +38,7 @@ impl<'a> Lexer<'a> { } self.next_token() } + Some('=') if self.peek_char() == Some('=') => self.double_char_token(Token::Equal), Some('=') => self.single_char_token(Token::Assign), Some(',') => self.single_char_token(Token::Comma), Some(':') => self.single_char_token(Token::Colon), diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 2544390520c..a770b4cad40 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -165,3 +165,15 @@ acir(inline) fn main f0 -> i32 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_constrain() { + let src: &str = " +acir(inline) fn main f0 { + b0(v0: Field): + constrain v0 == Field 1 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 3ce70b7e11b..852d7e0c6d7 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -51,6 +51,8 @@ pub(crate) enum Token { Semicolon, /// -> Arrow, + /// == + Equal, Eof, } @@ -71,6 +73,7 @@ pub(crate) enum Keyword { Brillig, Call, Cast, + Constrain, Inline, InlineAlways, Else, @@ -93,6 +96,7 @@ impl Keyword { "brillig" => Keyword::Brillig, "call" => Keyword::Call, "cast" => Keyword::Cast, + "constrain" => Keyword::Constrain, "else" => Keyword::Else, "inline" => Keyword::Inline, "inline_always" => Keyword::InlineAlways, From 989fc0e3e7006aca85afb9053ad83774c24f70bd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 14:40:22 -0300 Subject: [PATCH 18/65] Simplify one test --- .../src/ssa/opt/constant_folding.rs | 54 +++++++------------ compiler/noirc_evaluator/src/ssa/parser.rs | 2 +- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 3b86ded4a87..df94df83246 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -323,6 +323,7 @@ mod test { types::Type, value::{Value, ValueId}, }, + Ssa, }; use acvm::{acir::AcirField, FieldElement}; @@ -538,47 +539,30 @@ mod test { #[test] fn instruction_deduplication() { - // fn main f0 { - // b0(v0: u16): - // v1 = cast v0 as u32 - // v2 = cast v0 as u32 - // constrain v1 v2 - // } - // // After constructing this IR, we run constant folding which should replace the second cast // with a reference to the results to the first. This then allows us to optimize away // the constrain instruction as both inputs are known to be equal. // // The first cast instruction is retained and will be removed in the dead instruction elimination pass. - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::unsigned(16)); - - let v1 = builder.insert_cast(v0, Type::unsigned(32)); - let v2 = builder.insert_cast(v0, Type::unsigned(32)); - builder.insert_constrain(v1, v2, None); - - let mut ssa = builder.finish(); - let main = ssa.main_mut(); - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 3); - - // Expected output: - // - // fn main f0 { - // b0(v0: u16): - // v1 = cast v0 as u32 - // } + let src = " +acir(inline) fn main f0 { + b0(v0: u16): + v1 = cast v0 as u32 + v2 = cast v0 as u32 + constrain v1 == v2 + return +} + "; + let expected = " +acir(inline) fn main f0 { + b0(v0: u16): + v4 = cast v0 as u32 + return +} + "; + let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); - let main = ssa.main(); - let instructions = main.dfg[main.entry_block()].instructions(); - - assert_eq!(instructions.len(), 1); - let instruction = &main.dfg[instructions[0]]; - - assert_eq!(instruction, &Instruction::Cast(v0, Type::unsigned(32))); + assert_eq!(ssa.to_string().trim(), expected.trim()); } #[test] diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 83ac0c38f36..0ff4d55ed6a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -21,7 +21,7 @@ mod tests; mod token; impl Ssa { - fn from_str(str: &str) -> Result { + pub(crate) fn from_str(str: &str) -> Result { let mut parser = Parser::new(str).map_err(SsaError::ParserError)?; let parsed_ssa = parser.parse_ssa().map_err(SsaError::ParserError)?; parsed_ssa.into_ssa() From 3907e7f83dc6938f292d830592fe7cfd61ab5d11 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 14:45:40 -0300 Subject: [PATCH 19/65] enable_side_effects --- compiler/noirc_evaluator/src/ssa/parser.rs | 9 +++++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 1 + compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 4 ++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 4 ++++ 5 files changed, 30 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 0ff4d55ed6a..ea5bf75be5b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -174,6 +174,11 @@ impl<'a> Parser<'a> { return Ok(Some(ParsedInstruction::Constrain { lhs, rhs })); } + if self.eat_keyword(Keyword::EnableSideEffects)? { + let condition = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::EnableSideEffectsIf { condition })); + } + if let Some(target) = self.eat_identifier()? { let mut targets = vec![target]; @@ -385,6 +390,10 @@ impl<'a> Parser<'a> { } fn parse_type(&mut self) -> ParseResult { + if self.eat_keyword(Keyword::Bool)? { + return Ok(Type::bool()); + } + if self.eat_keyword(Keyword::Field)? { return Ok(Type::field()); } diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index b1b2bf95173..b5566a1fa2a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -48,6 +48,7 @@ pub(crate) enum ParsedInstruction { Call { targets: Vec, function: Identifier, arguments: Vec }, Cast { target: Identifier, lhs: ParsedValue, typ: Type }, Constrain { lhs: ParsedValue, rhs: ParsedValue }, + EnableSideEffectsIf { condition: ParsedValue }, } #[derive(Debug)] diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 8044171391b..5c84002070c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -167,6 +167,10 @@ impl Translator { let rhs = self.translate_value(rhs)?; self.builder.insert_constrain(lhs, rhs, None); } + ParsedInstruction::EnableSideEffectsIf { condition } => { + let condition = self.translate_value(condition)?; + self.builder.insert_enable_side_effects_if(condition); + } } Ok(()) diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index a770b4cad40..db7ee8be5a3 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -177,3 +177,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_enable_side_effects() { + let src: &str = " +acir(inline) fn main f0 { + b0(v0: Field): + enable_side_effects v0 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 852d7e0c6d7..013070e7df3 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -70,6 +70,7 @@ impl Token { pub(crate) enum Keyword { Acir, As, + Bool, Brillig, Call, Cast, @@ -77,6 +78,7 @@ pub(crate) enum Keyword { Inline, InlineAlways, Else, + EnableSideEffects, Field, Fold, Fn, @@ -93,11 +95,13 @@ impl Keyword { let keyword = match word { "acir" => Keyword::Acir, "as" => Keyword::As, + "bool" => Keyword::Bool, "brillig" => Keyword::Brillig, "call" => Keyword::Call, "cast" => Keyword::Cast, "constrain" => Keyword::Constrain, "else" => Keyword::Else, + "enable_side_effects" => Keyword::EnableSideEffects, "inline" => Keyword::Inline, "inline_always" => Keyword::InlineAlways, "Field" => Keyword::Field, From 7ba73ffa7c51533837e4460f8dfcbabfba3dec3a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 15:10:29 -0300 Subject: [PATCH 20/65] array_get --- .../noirc_evaluator/src/ssa/ir/printer.rs | 7 ++- compiler/noirc_evaluator/src/ssa/parser.rs | 47 +++++++++++++++---- .../noirc_evaluator/src/ssa/parser/ast.rs | 1 + .../src/ssa/parser/into_ssa.rs | 6 +++ .../noirc_evaluator/src/ssa/parser/tests.rs | 12 +++++ .../noirc_evaluator/src/ssa/parser/token.rs | 4 ++ 6 files changed, 67 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index e930ef87e1e..f544382b1ae 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -165,12 +165,13 @@ pub(crate) fn display_instruction( write!(f, "{} = ", value_list(function, results))?; } - display_instruction_inner(function, &function.dfg[instruction], f) + display_instruction_inner(function, &function.dfg[instruction], results, f) } fn display_instruction_inner( function: &Function, instruction: &Instruction, + results: &[ValueId], f: &mut Formatter, ) -> Result { let show = |id| value(function, id); @@ -205,7 +206,9 @@ fn display_instruction_inner( writeln!(f, "enable_side_effects {}", show(*condition)) } Instruction::ArrayGet { array, index } => { - writeln!(f, "array_get {}, index {}", show(*array), show(*index)) + assert_eq!(results.len(), 1); + let typ = function.dfg.type_of_value(results[0]); + writeln!(f, "array_get {}, {}, index {}", typ, show(*array), show(*index)) } Instruction::ArraySet { array, index, value, mutable } => { let array = show(*array); diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index ea5bf75be5b..a2d5796e215 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -167,16 +167,12 @@ impl<'a> Parser<'a> { } fn parse_instruction(&mut self) -> ParseResult> { - if self.eat_keyword(Keyword::Constrain)? { - let lhs = self.parse_value_or_error()?; - self.eat_or_error(Token::Equal)?; - let rhs = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::Constrain { lhs, rhs })); + if let Some(instruction) = self.parse_constrain()? { + return Ok(Some(instruction)); } - if self.eat_keyword(Keyword::EnableSideEffects)? { - let condition = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::EnableSideEffectsIf { condition })); + if let Some(instruction) = self.parse_enable_side_effects()? { + return Ok(Some(instruction)); } if let Some(target) = self.eat_identifier()? { @@ -203,6 +199,21 @@ impl<'a> Parser<'a> { let target = targets.remove(0); + if self.eat_keyword(Keyword::ArrayGet)? { + let element_type = self.parse_type()?; + self.eat_or_error(Token::Comma)?; + let array = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::Index))?; + let index = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::ArrayGet { + target, + element_type, + array, + index, + })); + } + if self.eat_keyword(Keyword::Cast)? { let lhs = self.parse_value_or_error()?; self.eat_or_error(Token::Keyword(Keyword::As))?; @@ -216,6 +227,26 @@ impl<'a> Parser<'a> { Ok(None) } + fn parse_constrain(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::Constrain)? { + return Ok(None); + } + + let lhs = self.parse_value_or_error()?; + self.eat_or_error(Token::Equal)?; + let rhs = self.parse_value_or_error()?; + Ok(Some(ParsedInstruction::Constrain { lhs, rhs })) + } + + fn parse_enable_side_effects(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::EnableSideEffects)? { + return Ok(None); + } + + let condition = self.parse_value_or_error()?; + Ok(Some(ParsedInstruction::EnableSideEffectsIf { condition })) + } + fn parse_terminator(&mut self) -> ParseResult { if let Some(terminator) = self.parse_return()? { return Ok(terminator); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index b5566a1fa2a..d22c1299731 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -45,6 +45,7 @@ impl Identifier { #[derive(Debug)] pub(crate) enum ParsedInstruction { + ArrayGet { target: Identifier, element_type: Type, array: ParsedValue, index: ParsedValue }, Call { targets: Vec, function: Identifier, arguments: Vec }, Cast { target: Identifier, lhs: ParsedValue, typ: Type }, Constrain { lhs: ParsedValue, rhs: ParsedValue }, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 5c84002070c..b47172a2c91 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -136,6 +136,12 @@ impl Translator { fn translate_instruction(&mut self, instruction: ParsedInstruction) -> Result<(), SsaError> { match instruction { + ParsedInstruction::ArrayGet { target, element_type, array, index } => { + let array = self.translate_value(array)?; + let index = self.translate_value(index)?; + let value_id = self.builder.insert_array_get(array, index, element_type); + self.define_variable(target, value_id)?; + } ParsedInstruction::Call { targets, function, arguments } => { let (function_id, return_types) = self.lookup_function(function)?; let result_types = return_types.to_vec(); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index db7ee8be5a3..9c89df27ec3 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -189,3 +189,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_array_get() { + let src: &str = " +acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get Field, v0, index Field 0 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 013070e7df3..515f2b76de7 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -69,6 +69,7 @@ impl Token { #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Keyword { Acir, + ArrayGet, As, Bool, Brillig, @@ -82,6 +83,7 @@ pub(crate) enum Keyword { Field, Fold, Fn, + Index, Jmp, Jmpif, NoPredicates, @@ -94,6 +96,7 @@ impl Keyword { pub(crate) fn lookup_keyword(word: &str) -> Option { let keyword = match word { "acir" => Keyword::Acir, + "array_get" => Keyword::ArrayGet, "as" => Keyword::As, "bool" => Keyword::Bool, "brillig" => Keyword::Brillig, @@ -107,6 +110,7 @@ impl Keyword { "Field" => Keyword::Field, "fold" => Keyword::Fold, "fn" => Keyword::Fn, + "index" => Keyword::Index, "jmp" => Keyword::Jmp, "jmpif" => Keyword::Jmpif, "no_predicates" => Keyword::NoPredicates, From 61c0880d262530d0c861a5f004b4e12a7484806e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 15:15:49 -0300 Subject: [PATCH 21/65] Simplify another test --- .../src/ssa/opt/constant_folding.rs | 86 ++++++------------- 1 file changed, 27 insertions(+), 59 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index df94df83246..55fece94ff7 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -325,7 +325,7 @@ mod test { }, Ssa, }; - use acvm::{acir::AcirField, FieldElement}; + use acvm::acir::AcirField; #[test] fn simple_constant_fold() { @@ -567,69 +567,37 @@ acir(inline) fn main f0 { #[test] fn constant_index_array_access_deduplication() { - // fn main f0 { - // b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): - // enable_side_effects v2 - // v4 = array_get v0 u32 0 - // v5 = array_get v0 v1 - // enable_side_effects v3 - // v6 = array_get v0 u32 0 - // v7 = array_get v0 v1 - // constrain v4 v6 - // } - // // After constructing this IR, we run constant folding which should replace the second constant-index array get // with a reference to the results to the first. This then allows us to optimize away // the constrain instruction as both inputs are known to be equal. - // - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - - let v0 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 4)); - let v1 = builder.add_parameter(Type::unsigned(32)); - let v2 = builder.add_parameter(Type::unsigned(1)); - let v3 = builder.add_parameter(Type::unsigned(1)); - - let zero = builder.numeric_constant(FieldElement::zero(), Type::length_type()); - - builder.insert_enable_side_effects_if(v2); - let v4 = builder.insert_array_get(v0, zero, Type::field()); - let _v5 = builder.insert_array_get(v0, v1, Type::field()); - - builder.insert_enable_side_effects_if(v3); - let v6 = builder.insert_array_get(v0, zero, Type::field()); - let _v7 = builder.insert_array_get(v0, v1, Type::field()); - - builder.insert_constrain(v4, v6, None); - - let ssa = builder.finish(); - - println!("{ssa}"); - - let main = ssa.main(); - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 7); + let src = " +acir(inline) fn main f0 { + b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): + enable_side_effects v2 + v4 = array_get Field, v0, index u32 0 + v5 = array_get Field, v0, index v1 + enable_side_effects v3 + v6 = array_get Field, v0, index u32 0 + v7 = array_get Field, v0, index v1 + constrain v4 == v6 + return +} + "; + let expected = " +acir(inline) fn main f0 { + b0(v0: [Field; 4], v1: u32, v2: u1, v3: u1): + enable_side_effects v2 + v10 = array_get Field, v0, index u32 0 + v11 = array_get Field, v0, index v1 + enable_side_effects v3 + v12 = array_get Field, v0, index v1 + return +} + "; - // Expected output: - // - // fn main f0 { - // b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): - // enable_side_effects v2 - // v10 = array_get v0 u32 0 - // v11 = array_get v0 v1 - // enable_side_effects v3 - // v12 = array_get v0 v1 - // } + let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); - - println!("{ssa}"); - - let main = ssa.main(); - let instructions = main.dfg[main.entry_block()].instructions(); - - assert_eq!(instructions.len(), 5); + assert_eq!(ssa.to_string().trim(), expected.trim()); } #[test] From 3601c776599e268ab99551bbe776baf65e62f90f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 15:24:21 -0300 Subject: [PATCH 22/65] Slightly better tests --- Cargo.lock | 1 + compiler/noirc_evaluator/Cargo.toml | 1 + compiler/noirc_evaluator/src/ssa.rs | 1 + .../src/ssa/opt/constant_folding.rs | 13 +++++++------ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 2 +- compiler/noirc_evaluator/src/ssa/tests.rs | 16 ++++++++++++++++ 6 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 compiler/noirc_evaluator/src/ssa/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 506a62b410d..ce6730f1f58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2941,6 +2941,7 @@ dependencies = [ "serde", "serde_json", "serde_with", + "similar-asserts", "thiserror", "tracing", ] diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index 509cca24e15..aac07339dd6 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -32,6 +32,7 @@ cfg-if.workspace = true [dev-dependencies] proptest.workspace = true +similar-asserts.workspace = true [features] bn254 = ["noirc_frontend/bn254"] diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index af3b7a0d675..b26d5caf9ba 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -47,6 +47,7 @@ pub mod ir; mod opt; mod parser; pub mod ssa_gen; +mod tests; pub struct SsaEvaluatorOptions { /// Emit debug information for the intermediate SSA IR diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 55fece94ff7..53b2648fbef 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -323,6 +323,7 @@ mod test { types::Type, value::{Value, ValueId}, }, + tests::assert_ssa_equals, Ssa, }; use acvm::acir::AcirField; @@ -556,13 +557,13 @@ acir(inline) fn main f0 { let expected = " acir(inline) fn main f0 { b0(v0: u16): - v4 = cast v0 as u32 + v1 = cast v0 as u32 return } "; let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); - assert_eq!(ssa.to_string().trim(), expected.trim()); + assert_ssa_equals(ssa, expected); } #[test] @@ -587,17 +588,17 @@ acir(inline) fn main f0 { acir(inline) fn main f0 { b0(v0: [Field; 4], v1: u32, v2: u1, v3: u1): enable_side_effects v2 - v10 = array_get Field, v0, index u32 0 - v11 = array_get Field, v0, index v1 + v5 = array_get Field, v0, index u32 0 + v6 = array_get Field, v0, index v1 enable_side_effects v3 - v12 = array_get Field, v0, index v1 + v7 = array_get Field, v0, index v1 return } "; let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); - assert_eq!(ssa.to_string().trim(), expected.trim()); + assert_ssa_equals(ssa, expected); } #[test] diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 9c89df27ec3..967879118d4 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -9,7 +9,7 @@ fn assert_ssa_roundtrip(src: &str) { let src = src.trim(); if ssa != src { println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", src, ssa); - assert_eq!(ssa, src); + similar_asserts::assert_eq!(ssa, src); } } diff --git a/compiler/noirc_evaluator/src/ssa/tests.rs b/compiler/noirc_evaluator/src/ssa/tests.rs new file mode 100644 index 00000000000..56d689b5a43 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/tests.rs @@ -0,0 +1,16 @@ +#![cfg(test)] + +use super::Ssa; + +pub(crate) fn assert_ssa_equals(mut ssa: Ssa, expected: &str) { + ssa.normalize_ids(); + + let ssa = ssa.to_string(); + let ssa = ssa.trim(); + let expected = expected.trim(); + + if ssa != expected { + println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", expected, ssa); + similar_asserts::assert_eq!(expected, ssa); + } +} From 4a190cab20317548be1a52564fe2e380f1ea3c82 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 15:26:22 -0300 Subject: [PATCH 23/65] Better place for test helper --- compiler/noirc_evaluator/src/ssa.rs | 1 - .../src/ssa/opt/constant_folding.rs | 2 +- compiler/noirc_evaluator/src/ssa/opt/mod.rs | 15 +++++++++++++++ compiler/noirc_evaluator/src/ssa/tests.rs | 16 ---------------- 4 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 compiler/noirc_evaluator/src/ssa/tests.rs diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index b26d5caf9ba..af3b7a0d675 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -47,7 +47,6 @@ pub mod ir; mod opt; mod parser; pub mod ssa_gen; -mod tests; pub struct SsaEvaluatorOptions { /// Emit debug information for the intermediate SSA IR diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 53b2648fbef..a7c05023c05 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -323,7 +323,7 @@ mod test { types::Type, value::{Value, ValueId}, }, - tests::assert_ssa_equals, + opt::assert_ssa_equals, Ssa, }; use acvm::acir::AcirField; diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index f3dbd58fa69..6037f7abfed 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -3,6 +3,7 @@ //! Each pass is generally expected to mutate the SSA IR into a gradually //! simpler form until the IR only has a single function remaining with 1 block within it. //! Generally, these passes are also expected to minimize the final amount of instructions. + mod array_set; mod as_slice_length; mod assert_constant; @@ -21,3 +22,17 @@ mod resolve_is_unconstrained; mod runtime_separation; mod simplify_cfg; mod unrolling; + +#[cfg(test)] +pub(crate) fn assert_ssa_equals(mut ssa: super::Ssa, expected: &str) { + ssa.normalize_ids(); + + let ssa = ssa.to_string(); + let ssa = ssa.trim(); + let expected = expected.trim(); + + if ssa != expected { + println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", expected, ssa); + similar_asserts::assert_eq!(expected, ssa); + } +} diff --git a/compiler/noirc_evaluator/src/ssa/tests.rs b/compiler/noirc_evaluator/src/ssa/tests.rs deleted file mode 100644 index 56d689b5a43..00000000000 --- a/compiler/noirc_evaluator/src/ssa/tests.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![cfg(test)] - -use super::Ssa; - -pub(crate) fn assert_ssa_equals(mut ssa: Ssa, expected: &str) { - ssa.normalize_ids(); - - let ssa = ssa.to_string(); - let ssa = ssa.trim(); - let expected = expected.trim(); - - if ssa != expected { - println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", expected, ssa); - similar_asserts::assert_eq!(expected, ssa); - } -} From cf4e560ec3c30d4cc19f4584e4ef640abd55d71b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 15:54:59 -0300 Subject: [PATCH 24/65] binary --- compiler/noirc_evaluator/src/ssa/parser.rs | 64 ++++++++++++++++++- .../noirc_evaluator/src/ssa/parser/ast.rs | 3 +- .../src/ssa/parser/into_ssa.rs | 6 ++ .../noirc_evaluator/src/ssa/parser/tests.rs | 32 +++++++--- .../noirc_evaluator/src/ssa/parser/token.rs | 24 +++++++ 5 files changed, 119 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index a2d5796e215..ee7c467ffc7 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -1,6 +1,9 @@ use std::sync::Arc; -use super::{ir::types::Type, Ssa}; +use super::{ + ir::{instruction::BinaryOp, types::Type}, + Ssa, +}; use acvm::{AcirField, FieldElement}; use ast::{ @@ -221,12 +224,71 @@ impl<'a> Parser<'a> { return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); } + if let Some(op) = self.eat_binary_op()? { + let lhs = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + let rhs = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::BinaryOp { target, lhs, op, rhs })); + } + return self.expected_instruction_or_terminator(); } Ok(None) } + fn eat_binary_op(&mut self) -> ParseResult> { + if self.eat_keyword(Keyword::Add)? { + return Ok(Some(BinaryOp::Add)); + } + + if self.eat_keyword(Keyword::Sub)? { + return Ok(Some(BinaryOp::Sub)); + } + + if self.eat_keyword(Keyword::Mul)? { + return Ok(Some(BinaryOp::Mul)); + } + + if self.eat_keyword(Keyword::Div)? { + return Ok(Some(BinaryOp::Div)); + } + + if self.eat_keyword(Keyword::Eq)? { + return Ok(Some(BinaryOp::Eq)); + } + + if self.eat_keyword(Keyword::Mod)? { + return Ok(Some(BinaryOp::Mod)); + } + + if self.eat_keyword(Keyword::Lt)? { + return Ok(Some(BinaryOp::Lt)); + } + + if self.eat_keyword(Keyword::And)? { + return Ok(Some(BinaryOp::And)); + } + + if self.eat_keyword(Keyword::Or)? { + return Ok(Some(BinaryOp::Or)); + } + + if self.eat_keyword(Keyword::Xor)? { + return Ok(Some(BinaryOp::Xor)); + } + + if self.eat_keyword(Keyword::Shl)? { + return Ok(Some(BinaryOp::Shl)); + } + + if self.eat_keyword(Keyword::Shr)? { + return Ok(Some(BinaryOp::Shr)); + } + + Ok(None) + } + fn parse_constrain(&mut self) -> ParseResult> { if !self.eat_keyword(Keyword::Constrain)? { return Ok(None); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index d22c1299731..b63729021d3 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -1,7 +1,7 @@ use acvm::FieldElement; use noirc_errors::Span; -use crate::ssa::ir::{function::RuntimeType, types::Type}; +use crate::ssa::ir::{function::RuntimeType, instruction::BinaryOp, types::Type}; #[derive(Debug)] pub(crate) struct ParsedSsa { @@ -46,6 +46,7 @@ impl Identifier { #[derive(Debug)] pub(crate) enum ParsedInstruction { ArrayGet { target: Identifier, element_type: Type, array: ParsedValue, index: ParsedValue }, + BinaryOp { target: Identifier, lhs: ParsedValue, op: BinaryOp, rhs: ParsedValue }, Call { targets: Vec, function: Identifier, arguments: Vec }, Cast { target: Identifier, lhs: ParsedValue, typ: Type }, Constrain { lhs: ParsedValue, rhs: ParsedValue }, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index b47172a2c91..ff1199e4459 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -142,6 +142,12 @@ impl Translator { let value_id = self.builder.insert_array_get(array, index, element_type); self.define_variable(target, value_id)?; } + ParsedInstruction::BinaryOp { target, lhs, op, rhs } => { + let lhs = self.translate_value(lhs)?; + let rhs = self.translate_value(rhs)?; + let value_id = self.builder.insert_binary(lhs, op, rhs); + self.define_variable(target, value_id)?; + } ParsedInstruction::Call { targets, function, arguments } => { let (function_id, return_types) = self.lookup_function(function)?; let result_types = return_types.to_vec(); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 967879118d4..8b189123d0b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -96,7 +96,7 @@ acir(inline) fn main f0 -> (Field, Field) { #[test] fn test_multiple_blocks_and_jmp() { - let src: &str = " + let src = " acir(inline) fn main f0 -> Field { b0(): jmp b1(Field 1) @@ -109,7 +109,7 @@ acir(inline) fn main f0 -> Field { #[test] fn test_jmpif() { - let src: &str = " + let src = " acir(inline) fn main f0 { b0(v0: Field): jmpif v0 then: b1, else: b2 @@ -124,7 +124,7 @@ acir(inline) fn main f0 { #[test] fn test_call() { - let src: &str = " + let src = " acir(inline) fn main f0 -> Field { b0(v0: Field): v2 = call f1(v0) @@ -140,7 +140,7 @@ acir(inline) fn foo f1 -> Field { #[test] fn test_call_multiple_return_values() { - let src: &str = " + let src = " acir(inline) fn main f0 -> [Field; 3] { b0(): v1, v2 = call f1() @@ -156,7 +156,7 @@ acir(inline) fn foo f1 -> ([Field; 3], [Field; 1]) { #[test] fn test_cast() { - let src: &str = " + let src = " acir(inline) fn main f0 -> i32 { b0(v0: Field): v1 = cast v0 as i32 @@ -168,7 +168,7 @@ acir(inline) fn main f0 -> i32 { #[test] fn test_constrain() { - let src: &str = " + let src = " acir(inline) fn main f0 { b0(v0: Field): constrain v0 == Field 1 @@ -180,7 +180,7 @@ acir(inline) fn main f0 { #[test] fn test_enable_side_effects() { - let src: &str = " + let src = " acir(inline) fn main f0 { b0(v0: Field): enable_side_effects v0 @@ -192,7 +192,7 @@ acir(inline) fn main f0 { #[test] fn test_array_get() { - let src: &str = " + let src = " acir(inline) fn main f0 { b0(v0: [Field; 3]): v2 = array_get Field, v0, index Field 0 @@ -201,3 +201,19 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_binary() { + for op in ["add", "sub", "mul", "div", "eq", "mod", "lt", "and", "or", "xor", "shl", "shr"] { + let src = format!( + " +acir(inline) fn main f0 {{ + b0(v0: Field, v1: Field): + v2 = {op} v0, v1 + return +}} +" + ); + assert_ssa_roundtrip(&src); + } +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 515f2b76de7..527bf3c1d47 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -69,6 +69,8 @@ impl Token { #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Keyword { Acir, + Add, + And, ArrayGet, As, Bool, @@ -76,26 +78,38 @@ pub(crate) enum Keyword { Call, Cast, Constrain, + Div, Inline, InlineAlways, Else, EnableSideEffects, + Eq, Field, Fold, Fn, Index, Jmp, Jmpif, + Lt, + Mod, + Mul, NoPredicates, Of, + Or, Return, + Shl, + Shr, + Sub, Then, + Xor, } impl Keyword { pub(crate) fn lookup_keyword(word: &str) -> Option { let keyword = match word { "acir" => Keyword::Acir, + "add" => Keyword::Add, + "and" => Keyword::And, "array_get" => Keyword::ArrayGet, "as" => Keyword::As, "bool" => Keyword::Bool, @@ -103,8 +117,10 @@ impl Keyword { "call" => Keyword::Call, "cast" => Keyword::Cast, "constrain" => Keyword::Constrain, + "div" => Keyword::Div, "else" => Keyword::Else, "enable_side_effects" => Keyword::EnableSideEffects, + "eq" => Keyword::Eq, "inline" => Keyword::Inline, "inline_always" => Keyword::InlineAlways, "Field" => Keyword::Field, @@ -113,10 +129,18 @@ impl Keyword { "index" => Keyword::Index, "jmp" => Keyword::Jmp, "jmpif" => Keyword::Jmpif, + "lt" => Keyword::Lt, + "mod" => Keyword::Mod, + "mul" => Keyword::Mul, "no_predicates" => Keyword::NoPredicates, "of" => Keyword::Of, + "or" => Keyword::Or, "return" => Keyword::Return, + "shl" => Keyword::Shl, + "shr" => Keyword::Shr, + "sub" => Keyword::Sub, "then" => Keyword::Then, + "xor" => Keyword::Xor, _ => return None, }; Some(Token::Keyword(keyword)) From 733ae2851dc5d703b2e678b3e2900b7efc3685fd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 16:09:09 -0300 Subject: [PATCH 25/65] Simplify another test --- .../src/ssa/opt/constant_folding.rs | 60 +++++++------------ compiler/noirc_evaluator/src/ssa/parser.rs | 4 +- 2 files changed, 23 insertions(+), 41 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index a7c05023c05..023b4c28823 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -326,62 +326,44 @@ mod test { opt::assert_ssa_equals, Ssa, }; - use acvm::acir::AcirField; #[test] fn simple_constant_fold() { - // fn main f0 { - // b0(v0: Field): - // v1 = add v0, Field 1 - // v2 = mul v1, Field 3 - // return v2 - // } - // // After constructing this IR, we set the value of v0 to 2. // The expected return afterwards should be 9. - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::field()); - - let one = builder.field_constant(1u128); - let two = builder.field_constant(2u128); - let three = builder.field_constant(3u128); - - let v1 = builder.insert_binary(v0, BinaryOp::Add, one); - let v2 = builder.insert_binary(v1, BinaryOp::Mul, three); - builder.terminate_with_return(vec![v2]); - - let mut ssa = builder.finish(); + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + v1 = add v0, Field 1 + v2 = mul v1, Field 3 + return v2 +} + "; + let mut ssa = Ssa::from_str(src).unwrap(); let main = ssa.main_mut(); + let instructions = main.dfg[main.entry_block()].instructions(); assert_eq!(instructions.len(), 2); // The final return is not counted + let v0 = main.parameters()[0]; + let two = main.dfg.make_constant(2_u128.into(), Type::field()); + // Expected output: // // fn main f0 { - // b0(Field 2: Field): + // b0(v0: Field): // return Field 9 // } main.dfg.set_value_from_id(v0, two); + let expected = " +acir(inline) fn main f0 -> Field { + b0(v0: Field): + return Field 9 +} + "; let ssa = ssa.fold_constants(); - let main = ssa.main(); - let block = &main.dfg[main.entry_block()]; - assert_eq!(block.instructions().len(), 0); - - match block.terminator() { - Some(TerminatorInstruction::Return { return_values, .. }) => { - let value = main - .dfg - .get_numeric_constant(return_values[0]) - .expect("Expected constant 9") - .to_u128(); - assert_eq!(value, 9); - } - _ => unreachable!("b0 should have a return terminator"), - } + assert_ssa_equals(ssa, expected); } #[test] diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index ee7c467ffc7..1939213e83c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -24,8 +24,8 @@ mod tests; mod token; impl Ssa { - pub(crate) fn from_str(str: &str) -> Result { - let mut parser = Parser::new(str).map_err(SsaError::ParserError)?; + pub(crate) fn from_str(src: &str) -> Result { + let mut parser = Parser::new(src).map_err(SsaError::ParserError)?; let parsed_ssa = parser.parse_ssa().map_err(SsaError::ParserError)?; parsed_ssa.into_ssa() } From 4aac831278cef9138c14d131534b49803b4e15fc Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 16:20:56 -0300 Subject: [PATCH 26/65] truncate --- compiler/noirc_evaluator/src/ssa/parser.rs | 17 +++++++++++++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 1 + .../noirc_evaluator/src/ssa/parser/into_ssa.rs | 5 +++++ .../noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ .../noirc_evaluator/src/ssa/parser/token.rs | 8 ++++++++ 5 files changed, 43 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 1939213e83c..a383496fcf4 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -224,6 +224,23 @@ impl<'a> Parser<'a> { return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); } + if self.eat_keyword(Keyword::Truncate)? { + let value = self.parse_value_or_error()?; + self.eat_or_error(Token::Keyword(Keyword::To))?; + let bit_size = self.eat_int_or_error()?.to_u128() as u32; + self.eat_or_error(Token::Keyword(Keyword::Bits))?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::MaxBitSize))?; + self.eat_or_error(Token::Colon)?; + let max_bit_size = self.eat_int_or_error()?.to_u128() as u32; + return Ok(Some(ParsedInstruction::Truncate { + target, + value, + bit_size, + max_bit_size, + })); + } + if let Some(op) = self.eat_binary_op()? { let lhs = self.parse_value_or_error()?; self.eat_or_error(Token::Comma)?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index b63729021d3..f7dff82ab9e 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -51,6 +51,7 @@ pub(crate) enum ParsedInstruction { Cast { target: Identifier, lhs: ParsedValue, typ: Type }, Constrain { lhs: ParsedValue, rhs: ParsedValue }, EnableSideEffectsIf { condition: ParsedValue }, + Truncate { target: Identifier, value: ParsedValue, bit_size: u32, max_bit_size: u32 }, } #[derive(Debug)] diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index ff1199e4459..0bc5b9b6ed5 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -183,6 +183,11 @@ impl Translator { let condition = self.translate_value(condition)?; self.builder.insert_enable_side_effects_if(condition); } + ParsedInstruction::Truncate { target, value, bit_size, max_bit_size } => { + let value = self.translate_value(value)?; + let value_id = self.builder.insert_truncate(value, bit_size, max_bit_size); + self.define_variable(target, value_id)?; + } } Ok(()) diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 8b189123d0b..9e622291cb3 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -217,3 +217,15 @@ acir(inline) fn main f0 {{ assert_ssa_roundtrip(&src); } } + +#[test] +fn test_truncate() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + v1 = truncate v0 to 8 bits, max_bit_size: 16 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 527bf3c1d47..77f2a6bad5b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -73,6 +73,7 @@ pub(crate) enum Keyword { And, ArrayGet, As, + Bits, Bool, Brillig, Call, @@ -91,6 +92,7 @@ pub(crate) enum Keyword { Jmp, Jmpif, Lt, + MaxBitSize, Mod, Mul, NoPredicates, @@ -101,6 +103,8 @@ pub(crate) enum Keyword { Shr, Sub, Then, + To, + Truncate, Xor, } @@ -112,6 +116,7 @@ impl Keyword { "and" => Keyword::And, "array_get" => Keyword::ArrayGet, "as" => Keyword::As, + "bits" => Keyword::Bits, "bool" => Keyword::Bool, "brillig" => Keyword::Brillig, "call" => Keyword::Call, @@ -130,6 +135,7 @@ impl Keyword { "jmp" => Keyword::Jmp, "jmpif" => Keyword::Jmpif, "lt" => Keyword::Lt, + "max_bit_size" => Keyword::MaxBitSize, "mod" => Keyword::Mod, "mul" => Keyword::Mul, "no_predicates" => Keyword::NoPredicates, @@ -140,6 +146,8 @@ impl Keyword { "shr" => Keyword::Shr, "sub" => Keyword::Sub, "then" => Keyword::Then, + "to" => Keyword::To, + "truncate" => Keyword::Truncate, "xor" => Keyword::Xor, _ => return None, }; From d048cb1329417a03d0fccbd29d19cc33e10ab12d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 16:29:38 -0300 Subject: [PATCH 27/65] Simplify another test --- .../src/ssa/opt/constant_folding.rs | 64 +++++++------------ 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 023b4c28823..a2e039f349f 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -368,56 +368,40 @@ acir(inline) fn main f0 -> Field { #[test] fn redundant_truncation() { - // fn main f0 { - // b0(v0: u16, v1: u16): - // v2 = div v0, v1 - // v3 = truncate v2 to 8 bits, max_bit_size: 16 - // return v3 - // } - // // After constructing this IR, we set the value of v1 to 2^8. // The expected return afterwards should be v2. - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::unsigned(16)); - let v1 = builder.add_parameter(Type::unsigned(16)); - - // Note that this constant guarantees that `v0/constant < 2^8`. We then do not need to truncate the result. - let constant = 2_u128.pow(8); - let constant = builder.numeric_constant(constant, Type::field()); - - let v2 = builder.insert_binary(v0, BinaryOp::Div, v1); - let v3 = builder.insert_truncate(v2, 8, 16); - builder.terminate_with_return(vec![v3]); - - let mut ssa = builder.finish(); + let src = " +acir(inline) fn main f0 { + b0(v0: u16, v1: u16): + v2 = div v0, v1 + v3 = truncate v2 to 8 bits, max_bit_size: 16 + return v3 +} + "; + let mut ssa = Ssa::from_str(src).unwrap(); let main = ssa.main_mut(); + let instructions = main.dfg[main.entry_block()].instructions(); assert_eq!(instructions.len(), 2); // The final return is not counted - // Expected output: - // - // fn main f0 { - // b0(Field 2: Field): - // return Field 9 - // } - main.dfg.set_value_from_id(v1, constant); + let v1 = main.parameters()[1]; - let ssa = ssa.fold_constants(); - let main = ssa.main(); + // Note that this constant guarantees that `v0/constant < 2^8`. We then do not need to truncate the result. + let constant = 2_u128.pow(8); + let constant = main.dfg.make_constant(constant.into(), Type::field()); - println!("{ssa}"); + main.dfg.set_value_from_id(v1, constant); - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 1); - let instruction = &main.dfg[instructions[0]]; + let expected = " +acir(inline) fn main f0 -> u16 { + b0(v0: u16, v1: Field): + v3 = div v0, Field 256 + return v3 +} + "; - assert_eq!( - instruction, - &Instruction::Binary(Binary { lhs: v0, operator: BinaryOp::Div, rhs: constant }) - ); + let ssa = ssa.fold_constants(); + assert_ssa_equals(ssa, expected); } #[test] From 3ae58b2edf99b93c524e6cb3151a08c12dbefee6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 16:30:59 -0300 Subject: [PATCH 28/65] Fix serialization test --- compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs index 3dba6dc0a98..14db8d7177f 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs @@ -139,7 +139,7 @@ mod test { let deserialized_ssa: Ssa = serde_json::from_str(serialized_ssa).unwrap(); let actual_string = format!("{}", deserialized_ssa); - let expected_string = "acir(inline) fn main f0 {\n \ + let expected_string = "acir(inline) fn main f0 -> Field {\n \ b0(v0: Field):\n \ v3 = add v0, Field 1\n \ v4 = mul v3, Field 3\n \ From 38851519a4dc000d6826be99556b96aa587fa2a1 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 16:40:25 -0300 Subject: [PATCH 29/65] array_set --- compiler/noirc_evaluator/src/ssa/parser.rs | 11 +++++++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 1 + compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 7 +++++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 4 ++++ 5 files changed, 35 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index a383496fcf4..b457ed76caa 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -217,6 +217,17 @@ impl<'a> Parser<'a> { })); } + if self.eat_keyword(Keyword::ArraySet)? { + let array = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::Index))?; + let index = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::Value))?; + let value = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::ArraySet { target, array, index, value })); + } + if self.eat_keyword(Keyword::Cast)? { let lhs = self.parse_value_or_error()?; self.eat_or_error(Token::Keyword(Keyword::As))?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index f7dff82ab9e..06fdd003f79 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -46,6 +46,7 @@ impl Identifier { #[derive(Debug)] pub(crate) enum ParsedInstruction { ArrayGet { target: Identifier, element_type: Type, array: ParsedValue, index: ParsedValue }, + ArraySet { target: Identifier, array: ParsedValue, index: ParsedValue, value: ParsedValue }, BinaryOp { target: Identifier, lhs: ParsedValue, op: BinaryOp, rhs: ParsedValue }, Call { targets: Vec, function: Identifier, arguments: Vec }, Cast { target: Identifier, lhs: ParsedValue, typ: Type }, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 0bc5b9b6ed5..791badd0f3a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -142,6 +142,13 @@ impl Translator { let value_id = self.builder.insert_array_get(array, index, element_type); self.define_variable(target, value_id)?; } + ParsedInstruction::ArraySet { target, array, index, value } => { + let array = self.translate_value(array)?; + let index = self.translate_value(index)?; + let value = self.translate_value(value)?; + self.builder.insert_array_set(array, index, value); + self.define_variable(target, array)?; + } ParsedInstruction::BinaryOp { target, lhs, op, rhs } => { let lhs = self.translate_value(lhs)?; let rhs = self.translate_value(rhs)?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 9e622291cb3..7350cbe629b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -202,6 +202,18 @@ acir(inline) fn main f0 { assert_ssa_roundtrip(src); } +#[test] +fn test_array_set() { + let src = " +acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v3 = array_set v0, index Field 0, value Field 1 + return +} +"; + assert_ssa_roundtrip(src); +} + #[test] fn test_binary() { for op in ["add", "sub", "mul", "div", "eq", "mod", "lt", "and", "or", "xor", "shl", "shr"] { diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 77f2a6bad5b..92efda87054 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -72,6 +72,7 @@ pub(crate) enum Keyword { Add, And, ArrayGet, + ArraySet, As, Bits, Bool, @@ -105,6 +106,7 @@ pub(crate) enum Keyword { Then, To, Truncate, + Value, Xor, } @@ -115,6 +117,7 @@ impl Keyword { "add" => Keyword::Add, "and" => Keyword::And, "array_get" => Keyword::ArrayGet, + "array_set" => Keyword::ArraySet, "as" => Keyword::As, "bits" => Keyword::Bits, "bool" => Keyword::Bool, @@ -148,6 +151,7 @@ impl Keyword { "then" => Keyword::Then, "to" => Keyword::To, "truncate" => Keyword::Truncate, + "value" => Keyword::Value, "xor" => Keyword::Xor, _ => return None, }; From eb9c7068cff492c090767d1257d0b38b1a939403 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 16:53:42 -0300 Subject: [PATCH 30/65] Fix bug in array set --- compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 4 ++-- compiler/noirc_evaluator/src/ssa/parser/tests.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 791badd0f3a..f04198aacd6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -146,8 +146,8 @@ impl Translator { let array = self.translate_value(array)?; let index = self.translate_value(index)?; let value = self.translate_value(value)?; - self.builder.insert_array_set(array, index, value); - self.define_variable(target, array)?; + let value_id = self.builder.insert_array_set(array, index, value); + self.define_variable(target, value_id)?; } ParsedInstruction::BinaryOp { target, lhs, op, rhs } => { let lhs = self.translate_value(lhs)?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 7350cbe629b..67e6dcadf26 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -214,6 +214,19 @@ acir(inline) fn main f0 { assert_ssa_roundtrip(src); } +#[test] +fn test_array_get_set_bug() { + let src = " +acir(inline) fn main f0 { + b0(v0: [u32; 3]): + v3 = array_set v0, index u32 1, value u32 2 + v5 = array_get u32, v3, index u32 0 + return +} +"; + assert_ssa_roundtrip(src); +} + #[test] fn test_binary() { for op in ["add", "sub", "mul", "div", "eq", "mod", "lt", "and", "or", "xor", "shl", "shr"] { From 1182893c028abe0868808124ae1b40aa8439aed6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 8 Nov 2024 16:56:19 -0300 Subject: [PATCH 31/65] Simplify another test --- .../src/ssa/opt/constant_folding.rs | 112 +++++++----------- 1 file changed, 40 insertions(+), 72 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index a2e039f349f..b9c17d24f77 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -683,84 +683,52 @@ acir(inline) fn main f0 { #[test] fn deduplicate_instructions_with_predicates() { - // fn main f0 { - // b0(v0: bool, v1: bool, v2: [u32; 2]): - // enable_side_effects v0 - // v3 = array_get v2, index u32 0 - // v4 = array_set v2, index u32 1, value: u32 2 - // v5 = array_get v4, index u32 0 - // constrain_eq v3, v5 - // enable_side_effects v1 - // v6 = array_get v2, index u32 0 - // v7 = array_set v2, index u32 1, value: u32 2 - // v8 = array_get v7, index u32 0 - // constrain_eq v6, v8 - // enable_side_effects v0 - // v9 = array_get v2, index u32 0 - // v10 = array_set v2, index u32 1, value: u32 2 - // v11 = array_get v10, index u32 0 - // constrain_eq v9, v11 - // } - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - - let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::bool()); - let v2 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 2)); - - let zero = builder.numeric_constant(0u128, Type::length_type()); - let one = builder.numeric_constant(1u128, Type::length_type()); - let two = builder.numeric_constant(2u128, Type::length_type()); - - builder.insert_enable_side_effects_if(v0); - let v3 = builder.insert_array_get(v2, zero, Type::length_type()); - let v4 = builder.insert_array_set(v2, one, two); - let v5 = builder.insert_array_get(v4, zero, Type::length_type()); - builder.insert_constrain(v3, v5, None); - - builder.insert_enable_side_effects_if(v1); - let v6 = builder.insert_array_get(v2, zero, Type::length_type()); - let v7 = builder.insert_array_set(v2, one, two); - let v8 = builder.insert_array_get(v7, zero, Type::length_type()); - builder.insert_constrain(v6, v8, None); - - // We expect all these instructions after the 'enable side effects' instruction to be removed. - builder.insert_enable_side_effects_if(v0); - let v9 = builder.insert_array_get(v2, zero, Type::length_type()); - let v10 = builder.insert_array_set(v2, one, two); - let v11 = builder.insert_array_get(v10, zero, Type::length_type()); - builder.insert_constrain(v9, v11, None); - - let ssa = builder.finish(); - println!("{ssa}"); + let src = " +acir(inline) fn main f0 { + b0(v0: u1, v1: u1, v2: [Field; 2]): + enable_side_effects v0 + v6 = array_get u32, v2, index u32 0 + v7 = array_set v2, index u32 1, value u32 2 + v8 = array_get u32, v7, index u32 0 + constrain v6 == v8 + enable_side_effects v1 + v9 = array_get u32, v2, index u32 0 + v10 = array_set v2, index u32 1, value u32 2 + v11 = array_get u32, v10, index u32 0 + constrain v9 == v11 + enable_side_effects v0 + v12 = array_get u32, v2, index u32 0 + v13 = array_set v2, index u32 1, value u32 2 + v14 = array_get u32, v13, index u32 0 + constrain v12 == v14 + return +} + "; + let ssa = Ssa::from_str(src).unwrap(); let main = ssa.main(); let instructions = main.dfg[main.entry_block()].instructions(); assert_eq!(instructions.len(), 15); - // Expected output: - // - // fn main f0 { - // b0(v0: bool, v1: bool, v2: [Field; 2]): - // enable_side_effects v0 - // v3 = array_get v2, index Field 0 - // v4 = array_set v2, index Field 1, value: Field 2 - // v5 = array_get v4, index Field 0 - // constrain_eq v3, v5 - // enable_side_effects v1 - // v7 = array_set v2, index Field 1, value: Field 2 - // v8 = array_get v7, index Field 0 - // constrain_eq v3, v8 - // enable_side_effects v0 - // } - let ssa = ssa.fold_constants_using_constraints(); - println!("{ssa}"); + let expected = " +acir(inline) fn main f0 { + b0(v0: u1, v1: u1, v2: [Field; 2]): + enable_side_effects v0 + v4 = array_get u32, v2, index u32 0 + v7 = array_set v2, index u32 1, value u32 2 + v8 = array_get u32, v7, index u32 0 + constrain v4 == v8 + enable_side_effects v1 + v9 = array_set v2, index u32 1, value u32 2 + v10 = array_get u32, v9, index u32 0 + constrain v4 == v10 + enable_side_effects v0 + return +} + "; - let main = ssa.main(); - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 10); + let ssa = ssa.fold_constants_using_constraints(); + assert_ssa_equals(ssa, expected); } // This test currently fails. It being fixed will address the issue https://github.com/noir-lang/noir/issues/5756 From 85ccc2ac0acb6a2aeeb407d27eb6bc15c92e3985 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:03:24 -0300 Subject: [PATCH 32/65] More uniform way to specify types --- .../noirc_evaluator/src/ssa/ir/printer.rs | 26 ++++------ .../src/ssa/opt/constant_folding.rs | 18 +++---- compiler/noirc_evaluator/src/ssa/parser.rs | 12 ++--- .../noirc_evaluator/src/ssa/parser/ast.rs | 51 +++++++++++++++---- .../src/ssa/parser/into_ssa.rs | 30 ++++------- .../noirc_evaluator/src/ssa/parser/tests.rs | 30 +++++------ 6 files changed, 92 insertions(+), 75 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index f544382b1ae..f317dfc44d3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -18,21 +18,7 @@ use super::{ /// Helper function for Function's Display impl to pretty-print the function with the given formatter. pub(crate) fn display_function(function: &Function, f: &mut Formatter) -> Result { - write!(f, "{} fn {} {}", function.runtime(), function.name(), function.id())?; - - let return_value_ids = function.returns(); - if !return_value_ids.is_empty() { - let return_types = vecmap(return_value_ids, |id| function.dfg.type_of_value(*id)); - let return_types_str = - return_types.iter().map(|typ| typ.to_string()).collect::>().join(", "); - if return_types.len() == 1 { - write!(f, " -> {}", return_types_str)?; - } else { - write!(f, " -> ({})", return_types_str)?; - } - } - - writeln!(f, " {{")?; + writeln!(f, "{} fn {} {} {{", function.runtime(), function.name(), function.id())?; display_block_with_successors(function, function.entry_block(), &mut HashSet::new(), f)?; write!(f, "}}") } @@ -195,7 +181,13 @@ fn display_instruction_inner( } } Instruction::Call { func, arguments } => { - writeln!(f, "call {}({})", show(*func), value_list(function, arguments)) + let types = vecmap(results, |result| function.dfg.type_of_value(*result).to_string()); + let types = if types.len() == 1 { + types[0].to_string() + } else { + format!("({})", types.join(", ")) + }; + writeln!(f, "call {}({}) -> {}", show(*func), value_list(function, arguments), types) } Instruction::Allocate => writeln!(f, "allocate"), Instruction::Load { address } => writeln!(f, "load {}", show(*address)), @@ -208,7 +200,7 @@ fn display_instruction_inner( Instruction::ArrayGet { array, index } => { assert_eq!(results.len(), 1); let typ = function.dfg.type_of_value(results[0]); - writeln!(f, "array_get {}, {}, index {}", typ, show(*array), show(*index)) + writeln!(f, "array_get {}, index {} -> {}", show(*array), show(*index), typ) } Instruction::ArraySet { array, index, value, mutable } => { let array = show(*array); diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index b9c17d24f77..8115e909ac8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -357,7 +357,7 @@ acir(inline) fn main f0 { main.dfg.set_value_from_id(v0, two); let expected = " -acir(inline) fn main f0 -> Field { +acir(inline) fn main f0 { b0(v0: Field): return Field 9 } @@ -393,7 +393,7 @@ acir(inline) fn main f0 { main.dfg.set_value_from_id(v1, constant); let expected = " -acir(inline) fn main f0 -> u16 { +acir(inline) fn main f0 { b0(v0: u16, v1: Field): v3 = div v0, Field 256 return v3 @@ -541,11 +541,11 @@ acir(inline) fn main f0 { acir(inline) fn main f0 { b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): enable_side_effects v2 - v4 = array_get Field, v0, index u32 0 - v5 = array_get Field, v0, index v1 + v4 = array_get v0, index u32 0 -> Field + v5 = array_get v0, index v1 -> Field enable_side_effects v3 - v6 = array_get Field, v0, index u32 0 - v7 = array_get Field, v0, index v1 + v6 = array_get v0, index u32 0 -> Field + v7 = array_get v0, index v1 -> Field constrain v4 == v6 return } @@ -554,10 +554,10 @@ acir(inline) fn main f0 { acir(inline) fn main f0 { b0(v0: [Field; 4], v1: u32, v2: u1, v3: u1): enable_side_effects v2 - v5 = array_get Field, v0, index u32 0 - v6 = array_get Field, v0, index v1 + v5 = array_get v0, index u32 0 -> Field + v6 = array_get v0, index v1 -> Field enable_side_effects v3 - v7 = array_get Field, v0, index v1 + v7 = array_get v0, index v1 -> Field return } "; diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index b457ed76caa..97feea70129 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -72,15 +72,13 @@ impl<'a> Parser<'a> { let external_name = self.eat_ident_or_error()?; let internal_name = self.eat_ident_or_error()?; - let return_types = if self.eat(Token::Arrow)? { self.parse_types()? } else { Vec::new() }; - self.eat_or_error(Token::LeftBrace)?; let blocks = self.parse_blocks()?; self.eat_or_error(Token::RightBrace)?; - Ok(ParsedFunction { runtime_type, external_name, internal_name, return_types, blocks }) + Ok(ParsedFunction { runtime_type, external_name, internal_name, blocks }) } fn parse_runtime_type(&mut self) -> ParseResult { @@ -191,7 +189,9 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Call)? { let function = self.eat_identifier_or_error()?; let arguments = self.parse_arguments()?; - return Ok(Some(ParsedInstruction::Call { targets, function, arguments })); + self.eat_or_error(Token::Arrow)?; + let types = self.parse_types()?; + return Ok(Some(ParsedInstruction::Call { targets, function, arguments, types })); } if targets.len() > 1 { @@ -203,12 +203,12 @@ impl<'a> Parser<'a> { let target = targets.remove(0); if self.eat_keyword(Keyword::ArrayGet)? { - let element_type = self.parse_type()?; - self.eat_or_error(Token::Comma)?; let array = self.parse_value_or_error()?; self.eat_or_error(Token::Comma)?; self.eat_or_error(Token::Keyword(Keyword::Index))?; let index = self.parse_value_or_error()?; + self.eat_or_error(Token::Arrow)?; + let element_type = self.parse_type()?; return Ok(Some(ParsedInstruction::ArrayGet { target, element_type, diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 06fdd003f79..5775d5d248e 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -13,7 +13,6 @@ pub(crate) struct ParsedFunction { pub(crate) runtime_type: RuntimeType, pub(crate) external_name: String, pub(crate) internal_name: String, - pub(crate) return_types: Vec, pub(crate) blocks: Vec, } @@ -45,14 +44,48 @@ impl Identifier { #[derive(Debug)] pub(crate) enum ParsedInstruction { - ArrayGet { target: Identifier, element_type: Type, array: ParsedValue, index: ParsedValue }, - ArraySet { target: Identifier, array: ParsedValue, index: ParsedValue, value: ParsedValue }, - BinaryOp { target: Identifier, lhs: ParsedValue, op: BinaryOp, rhs: ParsedValue }, - Call { targets: Vec, function: Identifier, arguments: Vec }, - Cast { target: Identifier, lhs: ParsedValue, typ: Type }, - Constrain { lhs: ParsedValue, rhs: ParsedValue }, - EnableSideEffectsIf { condition: ParsedValue }, - Truncate { target: Identifier, value: ParsedValue, bit_size: u32, max_bit_size: u32 }, + ArrayGet { + target: Identifier, + element_type: Type, + array: ParsedValue, + index: ParsedValue, + }, + ArraySet { + target: Identifier, + array: ParsedValue, + index: ParsedValue, + value: ParsedValue, + }, + BinaryOp { + target: Identifier, + lhs: ParsedValue, + op: BinaryOp, + rhs: ParsedValue, + }, + Call { + targets: Vec, + function: Identifier, + arguments: Vec, + types: Vec, + }, + Cast { + target: Identifier, + lhs: ParsedValue, + typ: Type, + }, + Constrain { + lhs: ParsedValue, + rhs: ParsedValue, + }, + EnableSideEffectsIf { + condition: ParsedValue, + }, + Truncate { + target: Identifier, + value: ParsedValue, + bit_size: u32, + max_bit_size: u32, + }, } #[derive(Debug)] diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index f04198aacd6..d18de9543b9 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -9,7 +9,7 @@ use crate::ssa::{ use super::{ Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedSsa, ParsedTerminator, - ParsedValue, RuntimeType, Ssa, SsaError, Type, + ParsedValue, RuntimeType, Ssa, SsaError, }; impl ParsedSsa { @@ -27,8 +27,8 @@ impl ParsedSsa { struct Translator { builder: FunctionBuilder, - /// Maps function names to their ID and types - functions: HashMap)>, + /// Maps function names to their IDs + functions: HashMap, /// Maps block names to their IDs blocks: HashMap>, @@ -51,10 +51,7 @@ impl Translator { let function_id = FunctionId::new(function_id_counter); function_id_counter += 1; - functions.insert( - function.internal_name.clone(), - (function_id, function.return_types.clone()), - ); + functions.insert(function.internal_name.clone(), function_id); } let mut translator = @@ -65,7 +62,7 @@ impl Translator { } fn translate_function(&mut self, function: ParsedFunction) -> Result<(), SsaError> { - let (function_id, _) = self.functions[&function.internal_name]; + let function_id = self.functions[&function.internal_name]; let external_name = function.external_name.clone(); match function.runtime_type { @@ -155,15 +152,13 @@ impl Translator { let value_id = self.builder.insert_binary(lhs, op, rhs); self.define_variable(target, value_id)?; } - ParsedInstruction::Call { targets, function, arguments } => { - let (function_id, return_types) = self.lookup_function(function)?; - let result_types = return_types.to_vec(); + ParsedInstruction::Call { targets, function, arguments, types } => { + let function_id = self.lookup_function(function)?; let function_id = self.builder.import_function(function_id); let arguments = self.translate_values(arguments)?; - let value_ids = - self.builder.insert_call(function_id, arguments, result_types).to_vec(); + let value_ids = self.builder.insert_call(function_id, arguments, types).to_vec(); if value_ids.len() != targets.len() { return Err(SsaError::MismatchedReturnValues { @@ -257,12 +252,9 @@ impl Translator { } } - fn lookup_function( - &mut self, - identifier: Identifier, - ) -> Result<(FunctionId, &[Type]), SsaError> { - if let Some((function_id, types)) = self.functions.get(&identifier.name) { - Ok((*function_id, types)) + fn lookup_function(&mut self, identifier: Identifier) -> Result { + if let Some(function_id) = self.functions.get(&identifier.name) { + Ok(*function_id) } else { Err(SsaError::UnknownFunction(identifier)) } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 67e6dcadf26..9024fb14c1d 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -40,7 +40,7 @@ fn test_return_integer() { for typ in ["u1", "u8", "u16", "u32", "u64", "i1", "i8", "i16", "i32", "i64", "Field"] { let src = format!( " -acir(inline) fn main f0 -> {typ} {{ +acir(inline) fn main f0 {{ b0(): return {typ} 1 }} @@ -53,7 +53,7 @@ acir(inline) fn main f0 -> {typ} {{ #[test] fn test_return_array() { let src = " -acir(inline) fn main f0 -> [Field; 1] { +acir(inline) fn main f0 { b0(): return [Field 1] of Field } @@ -64,7 +64,7 @@ acir(inline) fn main f0 -> [Field; 1] { #[test] fn test_return_empty_array() { let src = " -acir(inline) fn main f0 -> [Field; 0] { +acir(inline) fn main f0 { b0(): return [] of Field } @@ -75,7 +75,7 @@ acir(inline) fn main f0 -> [Field; 0] { #[test] fn test_return_composite_array() { let src = " -acir(inline) fn main f0 -> [(Field, Field); 1] { +acir(inline) fn main f0 { b0(): return [Field 1, Field 2] of (Field, Field) } @@ -86,7 +86,7 @@ acir(inline) fn main f0 -> [(Field, Field); 1] { #[test] fn test_block_parameters() { let src = " -acir(inline) fn main f0 -> (Field, Field) { +acir(inline) fn main f0 { b0(v0: Field, v1: Field): return v0, v1 } @@ -97,7 +97,7 @@ acir(inline) fn main f0 -> (Field, Field) { #[test] fn test_multiple_blocks_and_jmp() { let src = " -acir(inline) fn main f0 -> Field { +acir(inline) fn main f0 { b0(): jmp b1(Field 1) b1(v1: Field): @@ -125,12 +125,12 @@ acir(inline) fn main f0 { #[test] fn test_call() { let src = " -acir(inline) fn main f0 -> Field { +acir(inline) fn main f0 { b0(v0: Field): - v2 = call f1(v0) + v2 = call f1(v0) -> Field return v2 } -acir(inline) fn foo f1 -> Field { +acir(inline) fn foo f1 { b0(v0: Field): return v0 } @@ -141,12 +141,12 @@ acir(inline) fn foo f1 -> Field { #[test] fn test_call_multiple_return_values() { let src = " -acir(inline) fn main f0 -> [Field; 3] { +acir(inline) fn main f0 { b0(): - v1, v2 = call f1() + v1, v2 = call f1() -> ([Field; 3], [Field; 1]) return v1 } -acir(inline) fn foo f1 -> ([Field; 3], [Field; 1]) { +acir(inline) fn foo f1 { b0(): return [Field 1, Field 2, Field 3] of Field, [Field 4] of Field } @@ -157,7 +157,7 @@ acir(inline) fn foo f1 -> ([Field; 3], [Field; 1]) { #[test] fn test_cast() { let src = " -acir(inline) fn main f0 -> i32 { +acir(inline) fn main f0 { b0(v0: Field): v1 = cast v0 as i32 return v1 @@ -195,7 +195,7 @@ fn test_array_get() { let src = " acir(inline) fn main f0 { b0(v0: [Field; 3]): - v2 = array_get Field, v0, index Field 0 + v2 = array_get v0, index Field 0 -> Field return } "; @@ -220,7 +220,7 @@ fn test_array_get_set_bug() { acir(inline) fn main f0 { b0(v0: [u32; 3]): v3 = array_set v0, index u32 1, value u32 2 - v5 = array_get u32, v3, index u32 0 + v5 = array_get v3, index u32 0 -> u32 return } "; From 241f0edb6fe0f617735349a8cc5d83f2c7e18beb Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:05:20 -0300 Subject: [PATCH 33/65] not --- compiler/noirc_evaluator/src/ssa/parser.rs | 5 +++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 4 ++++ compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 5 +++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 5 files changed, 28 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 97feea70129..4a8af119246 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -235,6 +235,11 @@ impl<'a> Parser<'a> { return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); } + if self.eat_keyword(Keyword::Not)? { + let value = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::Not { target, value })); + } + if self.eat_keyword(Keyword::Truncate)? { let value = self.parse_value_or_error()?; self.eat_or_error(Token::Keyword(Keyword::To))?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 5775d5d248e..bb3081c291c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -80,6 +80,10 @@ pub(crate) enum ParsedInstruction { EnableSideEffectsIf { condition: ParsedValue, }, + Not { + target: Identifier, + value: ParsedValue, + }, Truncate { target: Identifier, value: ParsedValue, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index d18de9543b9..12f15ca2372 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -185,6 +185,11 @@ impl Translator { let condition = self.translate_value(condition)?; self.builder.insert_enable_side_effects_if(condition); } + ParsedInstruction::Not { target, value } => { + let value = self.translate_value(value)?; + let value_id = self.builder.insert_not(value); + self.define_variable(target, value_id)?; + } ParsedInstruction::Truncate { target, value, bit_size, max_bit_size } => { let value = self.translate_value(value)?; let value_id = self.builder.insert_truncate(value, bit_size, max_bit_size); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 9024fb14c1d..c988e41dd6a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -254,3 +254,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_not() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + v1 = not v0 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 92efda87054..62d0b441502 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -97,6 +97,7 @@ pub(crate) enum Keyword { Mod, Mul, NoPredicates, + Not, Of, Or, Return, @@ -142,6 +143,7 @@ impl Keyword { "mod" => Keyword::Mod, "mul" => Keyword::Mul, "no_predicates" => Keyword::NoPredicates, + "not" => Keyword::Not, "of" => Keyword::Of, "or" => Keyword::Or, "return" => Keyword::Return, From 18ba63eb38641a41dabf75259a48e1e57c09a334 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:11:13 -0300 Subject: [PATCH 34/65] range_check --- compiler/noirc_evaluator/src/ssa/parser.rs | 15 +++++++++++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 4 ++++ .../noirc_evaluator/src/ssa/parser/into_ssa.rs | 4 ++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 5 files changed, 37 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 4a8af119246..cd8628fb962 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -175,6 +175,9 @@ impl<'a> Parser<'a> { if let Some(instruction) = self.parse_enable_side_effects()? { return Ok(Some(instruction)); } + if let Some(instruction) = self.parse_range_check()? { + return Ok(Some(instruction)); + } if let Some(target) = self.eat_identifier()? { let mut targets = vec![target]; @@ -342,6 +345,18 @@ impl<'a> Parser<'a> { Ok(Some(ParsedInstruction::EnableSideEffectsIf { condition })) } + fn parse_range_check(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::RangeCheck)? { + return Ok(None); + } + + let value = self.parse_value_or_error()?; + self.eat_or_error(Token::Keyword(Keyword::To))?; + let max_bit_size = self.eat_int_or_error()?.to_u128() as u32; + self.eat_or_error(Token::Keyword(Keyword::Bits))?; + Ok(Some(ParsedInstruction::RangeCheck { value, max_bit_size })) + } + fn parse_terminator(&mut self) -> ParseResult { if let Some(terminator) = self.parse_return()? { return Ok(terminator); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index bb3081c291c..8450e7c1c9c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -84,6 +84,10 @@ pub(crate) enum ParsedInstruction { target: Identifier, value: ParsedValue, }, + RangeCheck { + value: ParsedValue, + max_bit_size: u32, + }, Truncate { target: Identifier, value: ParsedValue, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 12f15ca2372..7542bbb7b82 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -190,6 +190,10 @@ impl Translator { let value_id = self.builder.insert_not(value); self.define_variable(target, value_id)?; } + ParsedInstruction::RangeCheck { value, max_bit_size } => { + let value = self.translate_value(value)?; + self.builder.insert_range_check(value, max_bit_size, None); + } ParsedInstruction::Truncate { target, value, bit_size, max_bit_size } => { let value = self.translate_value(value)?; let value_id = self.builder.insert_truncate(value, bit_size, max_bit_size); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index c988e41dd6a..df470d21e4f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -266,3 +266,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_range_check() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + range_check v0 to 8 bits + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 62d0b441502..2b43549beec 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -100,6 +100,7 @@ pub(crate) enum Keyword { Not, Of, Or, + RangeCheck, Return, Shl, Shr, @@ -146,6 +147,7 @@ impl Keyword { "not" => Keyword::Not, "of" => Keyword::Of, "or" => Keyword::Or, + "range_check" => Keyword::RangeCheck, "return" => Keyword::Return, "shl" => Keyword::Shl, "shr" => Keyword::Shr, From 19341b690d5645c6a947e1168184c6a874dd6be6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:15:35 -0300 Subject: [PATCH 35/65] Fix serialization roundtrip again --- compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs index 14db8d7177f..3dba6dc0a98 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs @@ -139,7 +139,7 @@ mod test { let deserialized_ssa: Ssa = serde_json::from_str(serialized_ssa).unwrap(); let actual_string = format!("{}", deserialized_ssa); - let expected_string = "acir(inline) fn main f0 -> Field {\n \ + let expected_string = "acir(inline) fn main f0 {\n \ b0(v0: Field):\n \ v3 = add v0, Field 1\n \ v4 = mul v3, Field 3\n \ From 5a27b52443e2f4f90c1cdd04dd196bd1ac5b4ff9 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:22:25 -0300 Subject: [PATCH 36/65] allocate --- compiler/noirc_evaluator/src/ssa/ir/printer.rs | 10 +++++++++- compiler/noirc_evaluator/src/ssa/parser.rs | 6 ++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 4 ++++ compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 4 ++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 6 files changed, 37 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index f317dfc44d3..e8f3f44a482 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -8,6 +8,8 @@ use acvm::acir::circuit::{ErrorSelector, STRING_ERROR_SELECTOR}; use acvm::acir::AcirField; use iter_extended::vecmap; +use crate::ssa::ir::types::Type; + use super::{ basic_block::BasicBlockId, dfg::DataFlowGraph, @@ -189,7 +191,13 @@ fn display_instruction_inner( }; writeln!(f, "call {}({}) -> {}", show(*func), value_list(function, arguments), types) } - Instruction::Allocate => writeln!(f, "allocate"), + Instruction::Allocate => { + assert_eq!(results.len(), 1); + let Type::Reference(typ) = function.dfg.type_of_value(results[0]) else { + panic!("Allocate instruction must have a reference type") + }; + writeln!(f, "allocate -> {}", typ) + } Instruction::Load { address } => writeln!(f, "load {}", show(*address)), Instruction::Store { address, value } => { writeln!(f, "store {} at {}", show(*value), show(*address)) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index cd8628fb962..f773997d35c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -205,6 +205,12 @@ impl<'a> Parser<'a> { let target = targets.remove(0); + if self.eat_keyword(Keyword::Allocate)? { + self.eat_or_error(Token::Arrow)?; + let typ = self.parse_type()?; + return Ok(Some(ParsedInstruction::Allocate { target, typ })); + } + if self.eat_keyword(Keyword::ArrayGet)? { let array = self.parse_value_or_error()?; self.eat_or_error(Token::Comma)?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 8450e7c1c9c..43983c70a1c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -44,6 +44,10 @@ impl Identifier { #[derive(Debug)] pub(crate) enum ParsedInstruction { + Allocate { + target: Identifier, + typ: Type, + }, ArrayGet { target: Identifier, element_type: Type, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 7542bbb7b82..92e49899729 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -133,6 +133,10 @@ impl Translator { fn translate_instruction(&mut self, instruction: ParsedInstruction) -> Result<(), SsaError> { match instruction { + ParsedInstruction::Allocate { target, typ } => { + let value_id = self.builder.insert_allocate(typ); + self.define_variable(target, value_id)?; + } ParsedInstruction::ArrayGet { target, element_type, array, index } => { let array = self.translate_value(array)?; let index = self.translate_value(index)?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index df470d21e4f..3c76fb9c95a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -278,3 +278,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_allocate() { + let src = " +acir(inline) fn main f0 { + b0(): + v0 = allocate -> [Field; 3] + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 2b43549beec..5be16b365c5 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -70,6 +70,7 @@ impl Token { pub(crate) enum Keyword { Acir, Add, + Allocate, And, ArrayGet, ArraySet, @@ -117,6 +118,7 @@ impl Keyword { let keyword = match word { "acir" => Keyword::Acir, "add" => Keyword::Add, + "allocate" => Keyword::Allocate, "and" => Keyword::And, "array_get" => Keyword::ArrayGet, "array_set" => Keyword::ArraySet, From 253897fc60b9ec62895b704eaba047075de75c26 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:27:17 -0300 Subject: [PATCH 37/65] load --- compiler/noirc_evaluator/src/ssa/ir/printer.rs | 6 +++++- compiler/noirc_evaluator/src/ssa/parser.rs | 7 +++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 5 +++++ compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 5 +++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 6 files changed, 36 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index e8f3f44a482..ef67315c227 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -198,7 +198,11 @@ fn display_instruction_inner( }; writeln!(f, "allocate -> {}", typ) } - Instruction::Load { address } => writeln!(f, "load {}", show(*address)), + Instruction::Load { address } => { + assert_eq!(results.len(), 1); + let typ = function.dfg.type_of_value(results[0]); + writeln!(f, "load {} -> {}", show(*address), typ) + } Instruction::Store { address, value } => { writeln!(f, "store {} at {}", show(*value), show(*address)) } diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index f773997d35c..86d2606cf6a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -244,6 +244,13 @@ impl<'a> Parser<'a> { return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); } + if self.eat_keyword(Keyword::Load)? { + let value = self.parse_value_or_error()?; + self.eat_or_error(Token::Arrow)?; + let typ = self.parse_type()?; + return Ok(Some(ParsedInstruction::Load { target, value, typ })); + } + if self.eat_keyword(Keyword::Not)? { let value = self.parse_value_or_error()?; return Ok(Some(ParsedInstruction::Not { target, value })); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 43983c70a1c..57eb5e62d76 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -84,6 +84,11 @@ pub(crate) enum ParsedInstruction { EnableSideEffectsIf { condition: ParsedValue, }, + Load { + target: Identifier, + value: ParsedValue, + typ: Type, + }, Not { target: Identifier, value: ParsedValue, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 92e49899729..47c35655fc5 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -189,6 +189,11 @@ impl Translator { let condition = self.translate_value(condition)?; self.builder.insert_enable_side_effects_if(condition); } + ParsedInstruction::Load { target, value, typ } => { + let value = self.translate_value(value)?; + let value_id = self.builder.insert_load(value, typ); + self.define_variable(target, value_id)?; + } ParsedInstruction::Not { target, value } => { let value = self.translate_value(value)?; let value_id = self.builder.insert_not(value); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 3c76fb9c95a..33ac2e822e2 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -290,3 +290,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_load() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + v1 = load v0 -> Field + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 5be16b365c5..abf224fa84d 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -93,6 +93,7 @@ pub(crate) enum Keyword { Index, Jmp, Jmpif, + Load, Lt, MaxBitSize, Mod, @@ -141,6 +142,7 @@ impl Keyword { "index" => Keyword::Index, "jmp" => Keyword::Jmp, "jmpif" => Keyword::Jmpif, + "load" => Keyword::Load, "lt" => Keyword::Lt, "max_bit_size" => Keyword::MaxBitSize, "mod" => Keyword::Mod, From 7b2870b4564048e739bc971a6e75127ba5c9440c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:34:14 -0300 Subject: [PATCH 38/65] store --- compiler/noirc_evaluator/src/ssa/parser.rs | 16 ++++++++++++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 4 ++++ .../noirc_evaluator/src/ssa/parser/into_ssa.rs | 5 +++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 4 ++++ 5 files changed, 41 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 86d2606cf6a..e3f2d123006 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -175,10 +175,15 @@ impl<'a> Parser<'a> { if let Some(instruction) = self.parse_enable_side_effects()? { return Ok(Some(instruction)); } + if let Some(instruction) = self.parse_range_check()? { return Ok(Some(instruction)); } + if let Some(instruction) = self.parse_store()? { + return Ok(Some(instruction)); + } + if let Some(target) = self.eat_identifier()? { let mut targets = vec![target]; @@ -370,6 +375,17 @@ impl<'a> Parser<'a> { Ok(Some(ParsedInstruction::RangeCheck { value, max_bit_size })) } + fn parse_store(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::Store)? { + return Ok(None); + } + + let value = self.parse_value_or_error()?; + self.eat_or_error(Token::Keyword(Keyword::At))?; + let address = self.parse_value_or_error()?; + Ok(Some(ParsedInstruction::Store { address, value })) + } + fn parse_terminator(&mut self) -> ParseResult { if let Some(terminator) = self.parse_return()? { return Ok(terminator); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 57eb5e62d76..cf43b510e87 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -97,6 +97,10 @@ pub(crate) enum ParsedInstruction { value: ParsedValue, max_bit_size: u32, }, + Store { + value: ParsedValue, + address: ParsedValue, + }, Truncate { target: Identifier, value: ParsedValue, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 47c35655fc5..9a7c0064285 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -203,6 +203,11 @@ impl Translator { let value = self.translate_value(value)?; self.builder.insert_range_check(value, max_bit_size, None); } + ParsedInstruction::Store { value, address } => { + let value = self.translate_value(value)?; + let address = self.translate_value(address)?; + self.builder.insert_store(address, value); + } ParsedInstruction::Truncate { target, value, bit_size, max_bit_size } => { let value = self.translate_value(value)?; let value_id = self.builder.insert_truncate(value, bit_size, max_bit_size); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 33ac2e822e2..7a7951d77db 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -302,3 +302,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_store() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + store Field 1 at v0 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index abf224fa84d..13ce2bdb6a6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -75,6 +75,7 @@ pub(crate) enum Keyword { ArrayGet, ArraySet, As, + At, Bits, Bool, Brillig, @@ -106,6 +107,7 @@ pub(crate) enum Keyword { Return, Shl, Shr, + Store, Sub, Then, To, @@ -124,6 +126,7 @@ impl Keyword { "array_get" => Keyword::ArrayGet, "array_set" => Keyword::ArraySet, "as" => Keyword::As, + "at" => Keyword::At, "bits" => Keyword::Bits, "bool" => Keyword::Bool, "brillig" => Keyword::Brillig, @@ -155,6 +158,7 @@ impl Keyword { "return" => Keyword::Return, "shl" => Keyword::Shl, "shr" => Keyword::Shr, + "store" => Keyword::Store, "sub" => Keyword::Sub, "then" => Keyword::Then, "to" => Keyword::To, From af832a588920c7e84189640957e20a0aa588b485 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:42:16 -0300 Subject: [PATCH 39/65] inc_rc --- compiler/noirc_evaluator/src/ssa/parser.rs | 13 +++++++++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 3 +++ compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 4 ++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 5 files changed, 34 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index e3f2d123006..f91cdf73f21 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -176,6 +176,10 @@ impl<'a> Parser<'a> { return Ok(Some(instruction)); } + if let Some(instruction) = self.parse_increment_rc()? { + return Ok(Some(instruction)); + } + if let Some(instruction) = self.parse_range_check()? { return Ok(Some(instruction)); } @@ -363,6 +367,15 @@ impl<'a> Parser<'a> { Ok(Some(ParsedInstruction::EnableSideEffectsIf { condition })) } + fn parse_increment_rc(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::IncRc)? { + return Ok(None); + } + + let value = self.parse_value_or_error()?; + Ok(Some(ParsedInstruction::IncrementRc { value })) + } + fn parse_range_check(&mut self) -> ParseResult> { if !self.eat_keyword(Keyword::RangeCheck)? { return Ok(None); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index cf43b510e87..2cee1279199 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -84,6 +84,9 @@ pub(crate) enum ParsedInstruction { EnableSideEffectsIf { condition: ParsedValue, }, + IncrementRc { + value: ParsedValue, + }, Load { target: Identifier, value: ParsedValue, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 9a7c0064285..0de621e6795 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -189,6 +189,10 @@ impl Translator { let condition = self.translate_value(condition)?; self.builder.insert_enable_side_effects_if(condition); } + ParsedInstruction::IncrementRc { value } => { + let value = self.translate_value(value)?; + self.builder.increment_array_reference_count(value); + } ParsedInstruction::Load { target, value, typ } => { let value = self.translate_value(value)?; let value_id = self.builder.insert_load(value, typ); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 7a7951d77db..0b4a6771cf4 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -314,3 +314,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_inc_rc() { + let src = " +acir(inline) fn main f0 { + b0(v0: [Field; 3]): + inc_rc v0 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 13ce2bdb6a6..13972e894cd 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -91,6 +91,7 @@ pub(crate) enum Keyword { Field, Fold, Fn, + IncRc, Index, Jmp, Jmpif, @@ -142,6 +143,7 @@ impl Keyword { "Field" => Keyword::Field, "fold" => Keyword::Fold, "fn" => Keyword::Fn, + "inc_rc" => Keyword::IncRc, "index" => Keyword::Index, "jmp" => Keyword::Jmp, "jmpif" => Keyword::Jmpif, From eb512ae926f5798709404431361144fd01acf801 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:46:14 -0300 Subject: [PATCH 40/65] dec_rc --- compiler/noirc_evaluator/src/ssa/parser.rs | 13 +++++++++++++ compiler/noirc_evaluator/src/ssa/parser/ast.rs | 3 +++ compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 4 ++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 5 files changed, 34 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index f91cdf73f21..f893b1da03a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -172,6 +172,10 @@ impl<'a> Parser<'a> { return Ok(Some(instruction)); } + if let Some(instruction) = self.parse_decrement_rc()? { + return Ok(Some(instruction)); + } + if let Some(instruction) = self.parse_enable_side_effects()? { return Ok(Some(instruction)); } @@ -358,6 +362,15 @@ impl<'a> Parser<'a> { Ok(Some(ParsedInstruction::Constrain { lhs, rhs })) } + fn parse_decrement_rc(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::DecRc)? { + return Ok(None); + } + + let value = self.parse_value_or_error()?; + Ok(Some(ParsedInstruction::DecrementRc { value })) + } + fn parse_enable_side_effects(&mut self) -> ParseResult> { if !self.eat_keyword(Keyword::EnableSideEffects)? { return Ok(None); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 2cee1279199..df3ddefc8a6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -81,6 +81,9 @@ pub(crate) enum ParsedInstruction { lhs: ParsedValue, rhs: ParsedValue, }, + DecrementRc { + value: ParsedValue, + }, EnableSideEffectsIf { condition: ParsedValue, }, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 0de621e6795..f0889693a2a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -185,6 +185,10 @@ impl Translator { let rhs = self.translate_value(rhs)?; self.builder.insert_constrain(lhs, rhs, None); } + ParsedInstruction::DecrementRc { value } => { + let value = self.translate_value(value)?; + self.builder.decrement_array_reference_count(value); + } ParsedInstruction::EnableSideEffectsIf { condition } => { let condition = self.translate_value(condition)?; self.builder.insert_enable_side_effects_if(condition); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 0b4a6771cf4..9f29b64dc0f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -326,3 +326,15 @@ acir(inline) fn main f0 { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_dec_rc() { + let src = " +acir(inline) fn main f0 { + b0(v0: [Field; 3]): + dec_rc v0 + return +} +"; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 13972e894cd..9b6e2240770 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -82,6 +82,7 @@ pub(crate) enum Keyword { Call, Cast, Constrain, + DecRc, Div, Inline, InlineAlways, @@ -134,6 +135,7 @@ impl Keyword { "call" => Keyword::Call, "cast" => Keyword::Cast, "constrain" => Keyword::Constrain, + "dec_rc" => Keyword::DecRc, "div" => Keyword::Div, "else" => Keyword::Else, "enable_side_effects" => Keyword::EnableSideEffects, From 962b80ea9b90bc82c49dfd6f91114b5d429fb612 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 16:56:05 -0300 Subject: [PATCH 41/65] call without returns --- compiler/noirc_evaluator/src/ssa/ir/printer.rs | 12 +++++++----- compiler/noirc_evaluator/src/ssa/parser.rs | 14 ++++++++++++++ compiler/noirc_evaluator/src/ssa/parser/tests.rs | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index ef67315c227..9d765c6a1e4 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -183,13 +183,15 @@ fn display_instruction_inner( } } Instruction::Call { func, arguments } => { + let arguments = value_list(function, arguments); let types = vecmap(results, |result| function.dfg.type_of_value(*result).to_string()); - let types = if types.len() == 1 { - types[0].to_string() + if types.is_empty() { + writeln!(f, "call {}({})", show(*func), arguments) + } else if types.len() == 1 { + writeln!(f, "call {}({}) -> {}", show(*func), arguments, types[0]) } else { - format!("({})", types.join(", ")) - }; - writeln!(f, "call {}({}) -> {}", show(*func), value_list(function, arguments), types) + writeln!(f, "call {}({}) -> ({})", show(*func), arguments, types.join(", ")) + } } Instruction::Allocate => { assert_eq!(results.len(), 1); diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index f893b1da03a..ced2e3c6a5b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -168,6 +168,10 @@ impl<'a> Parser<'a> { } fn parse_instruction(&mut self) -> ParseResult> { + if let Some(instruction) = self.parse_call()? { + return Ok(Some(instruction)); + } + if let Some(instruction) = self.parse_constrain()? { return Ok(Some(instruction)); } @@ -351,6 +355,16 @@ impl<'a> Parser<'a> { Ok(None) } + fn parse_call(&mut self) -> ParseResult> { + if !self.eat_keyword(Keyword::Call)? { + return Ok(None); + } + + let function = self.eat_identifier_or_error()?; + let arguments = self.parse_arguments()?; + Ok(Some(ParsedInstruction::Call { targets: vec![], function, arguments, types: vec![] })) + } + fn parse_constrain(&mut self) -> ParseResult> { if !self.eat_keyword(Keyword::Constrain)? { return Ok(None); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 9f29b64dc0f..0f5c3ea23a2 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -154,6 +154,22 @@ acir(inline) fn foo f1 { assert_ssa_roundtrip(src); } +#[test] +fn test_call_no_return_value() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + call f1(v0) + return +} +acir(inline) fn foo f1 { + b0(v0: Field): + return +} +"; + assert_ssa_roundtrip(src); +} + #[test] fn test_cast() { let src = " From 2d06011cbbee7d595011439bb91935e047152dbe Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 9 Nov 2024 17:07:46 -0300 Subject: [PATCH 42/65] Intrinsic --- compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 8 ++++++-- compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index f0889693a2a..43c33de9c08 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -157,9 +157,13 @@ impl Translator { self.define_variable(target, value_id)?; } ParsedInstruction::Call { targets, function, arguments, types } => { - let function_id = self.lookup_function(function)?; + let function_id = if let Some(id) = self.builder.import_intrinsic(&function.name) { + id + } else { + let function_id = self.lookup_function(function)?; + self.builder.import_function(function_id) + }; - let function_id = self.builder.import_function(function_id); let arguments = self.translate_values(arguments)?; let value_ids = self.builder.insert_call(function_id, arguments, types).to_vec(); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 0f5c3ea23a2..3ea77fdf9c4 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -170,6 +170,18 @@ acir(inline) fn foo f1 { assert_ssa_roundtrip(src); } +#[test] +fn test_call_intrinsic() { + let src = " +acir(inline) fn main f0 { + b0(v0: Field): + call assert_constant(v0) + return +} +"; + assert_ssa_roundtrip(src); +} + #[test] fn test_cast() { let src = " From 543acadba8485fc071ff820c948824474691767c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 06:56:53 -0300 Subject: [PATCH 43/65] Trim leading whitespace from lines for better-looking tests --- compiler/noirc_evaluator/src/lib.rs | 18 + .../src/ssa/opt/constant_folding.rs | 128 +++---- compiler/noirc_evaluator/src/ssa/opt/mod.rs | 6 +- .../noirc_evaluator/src/ssa/parser/tests.rs | 362 +++++++++--------- 4 files changed, 264 insertions(+), 250 deletions(-) diff --git a/compiler/noirc_evaluator/src/lib.rs b/compiler/noirc_evaluator/src/lib.rs index 54376963338..453e8b59f58 100644 --- a/compiler/noirc_evaluator/src/lib.rs +++ b/compiler/noirc_evaluator/src/lib.rs @@ -12,3 +12,21 @@ pub mod ssa; pub mod brillig; pub use ssa::create_program; + +/// Trims leading whitespace from each line of the input string, according to +/// how much leading whitespace there is on the first non-empty line. +#[cfg(test)] +pub(crate) fn trim_leading_whitespace_from_lines(src: &str) -> String { + let mut lines = src.trim_end().lines(); + let mut first_line = lines.next().unwrap(); + while first_line.is_empty() { + first_line = lines.next().unwrap(); + } + let indent = first_line.len() - first_line.trim_start().len(); + let mut result = first_line.trim_start().to_string(); + for line in lines { + result.push('\n'); + result.push_str(&line[indent..]); + } + result +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 8115e909ac8..e2c81cce7cd 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -332,13 +332,13 @@ mod test { // After constructing this IR, we set the value of v0 to 2. // The expected return afterwards should be 9. let src = " -acir(inline) fn main f0 { - b0(v0: Field): - v1 = add v0, Field 1 - v2 = mul v1, Field 3 - return v2 -} - "; + acir(inline) fn main f0 { + b0(v0: Field): + v1 = add v0, Field 1 + v2 = mul v1, Field 3 + return v2 + } + "; let mut ssa = Ssa::from_str(src).unwrap(); let main = ssa.main_mut(); @@ -348,20 +348,14 @@ acir(inline) fn main f0 { let v0 = main.parameters()[0]; let two = main.dfg.make_constant(2_u128.into(), Type::field()); - // Expected output: - // - // fn main f0 { - // b0(v0: Field): - // return Field 9 - // } main.dfg.set_value_from_id(v0, two); let expected = " -acir(inline) fn main f0 { - b0(v0: Field): - return Field 9 -} - "; + acir(inline) fn main f0 { + b0(v0: Field): + return Field 9 + } + "; let ssa = ssa.fold_constants(); assert_ssa_equals(ssa, expected); } @@ -371,13 +365,13 @@ acir(inline) fn main f0 { // After constructing this IR, we set the value of v1 to 2^8. // The expected return afterwards should be v2. let src = " -acir(inline) fn main f0 { - b0(v0: u16, v1: u16): - v2 = div v0, v1 - v3 = truncate v2 to 8 bits, max_bit_size: 16 - return v3 -} - "; + acir(inline) fn main f0 { + b0(v0: u16, v1: u16): + v2 = div v0, v1 + v3 = truncate v2 to 8 bits, max_bit_size: 16 + return v3 + } + "; let mut ssa = Ssa::from_str(src).unwrap(); let main = ssa.main_mut(); @@ -393,12 +387,12 @@ acir(inline) fn main f0 { main.dfg.set_value_from_id(v1, constant); let expected = " -acir(inline) fn main f0 { - b0(v0: u16, v1: Field): - v3 = div v0, Field 256 - return v3 -} - "; + acir(inline) fn main f0 { + b0(v0: u16, v1: Field): + v3 = div v0, Field 256 + return v3 + } + "; let ssa = ssa.fold_constants(); assert_ssa_equals(ssa, expected); @@ -512,21 +506,21 @@ acir(inline) fn main f0 { // // The first cast instruction is retained and will be removed in the dead instruction elimination pass. let src = " -acir(inline) fn main f0 { - b0(v0: u16): - v1 = cast v0 as u32 - v2 = cast v0 as u32 - constrain v1 == v2 - return -} - "; + acir(inline) fn main f0 { + b0(v0: u16): + v1 = cast v0 as u32 + v2 = cast v0 as u32 + constrain v1 == v2 + return + } + "; let expected = " -acir(inline) fn main f0 { - b0(v0: u16): - v1 = cast v0 as u32 - return -} - "; + acir(inline) fn main f0 { + b0(v0: u16): + v1 = cast v0 as u32 + return + } + "; let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); assert_ssa_equals(ssa, expected); @@ -538,29 +532,29 @@ acir(inline) fn main f0 { // with a reference to the results to the first. This then allows us to optimize away // the constrain instruction as both inputs are known to be equal. let src = " -acir(inline) fn main f0 { - b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): - enable_side_effects v2 - v4 = array_get v0, index u32 0 -> Field - v5 = array_get v0, index v1 -> Field - enable_side_effects v3 - v6 = array_get v0, index u32 0 -> Field - v7 = array_get v0, index v1 -> Field - constrain v4 == v6 - return -} - "; + acir(inline) fn main f0 { + b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): + enable_side_effects v2 + v4 = array_get v0, index u32 0 -> Field + v5 = array_get v0, index v1 -> Field + enable_side_effects v3 + v6 = array_get v0, index u32 0 -> Field + v7 = array_get v0, index v1 -> Field + constrain v4 == v6 + return + } + "; let expected = " -acir(inline) fn main f0 { - b0(v0: [Field; 4], v1: u32, v2: u1, v3: u1): - enable_side_effects v2 - v5 = array_get v0, index u32 0 -> Field - v6 = array_get v0, index v1 -> Field - enable_side_effects v3 - v7 = array_get v0, index v1 -> Field - return -} - "; + acir(inline) fn main f0 { + b0(v0: [Field; 4], v1: u32, v2: u1, v3: u1): + enable_side_effects v2 + v5 = array_get v0, index u32 0 -> Field + v6 = array_get v0, index v1 -> Field + enable_side_effects v3 + v7 = array_get v0, index v1 -> Field + return + } + "; let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 6037f7abfed..c82687374e4 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -25,11 +25,13 @@ mod unrolling; #[cfg(test)] pub(crate) fn assert_ssa_equals(mut ssa: super::Ssa, expected: &str) { + use crate::trim_leading_whitespace_from_lines; + ssa.normalize_ids(); let ssa = ssa.to_string(); - let ssa = ssa.trim(); - let expected = expected.trim(); + let ssa = trim_leading_whitespace_from_lines(&ssa); + let expected = trim_leading_whitespace_from_lines(expected); if ssa != expected { println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", expected, ssa); diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 3ea77fdf9c4..061110130e1 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -1,12 +1,12 @@ #![cfg(test)] -use crate::ssa::Ssa; +use crate::{ssa::Ssa, trim_leading_whitespace_from_lines}; fn assert_ssa_roundtrip(src: &str) { let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.to_string(); - let ssa = ssa.trim(); - let src = src.trim(); + let ssa = trim_leading_whitespace_from_lines(&ssa); + let src = trim_leading_whitespace_from_lines(src); if ssa != src { println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", src, ssa); similar_asserts::assert_eq!(ssa, src); @@ -16,22 +16,22 @@ fn assert_ssa_roundtrip(src: &str) { #[test] fn test_empty_acir_function() { let src = " -acir(inline) fn main f0 { - b0(): - return -} -"; + acir(inline) fn main f0 { + b0(): + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_empty_brillig_function() { let src = " -brillig(inline) fn main f0 { - b0(): - return -} -"; + brillig(inline) fn main f0 { + b0(): + return + } + "; assert_ssa_roundtrip(src); } @@ -40,11 +40,11 @@ fn test_return_integer() { for typ in ["u1", "u8", "u16", "u32", "u64", "i1", "i8", "i16", "i32", "i64", "Field"] { let src = format!( " -acir(inline) fn main f0 {{ - b0(): - return {typ} 1 -}} -" + acir(inline) fn main f0 {{ + b0(): + return {typ} 1 + }} + " ); assert_ssa_roundtrip(&src); } @@ -53,205 +53,205 @@ acir(inline) fn main f0 {{ #[test] fn test_return_array() { let src = " -acir(inline) fn main f0 { - b0(): - return [Field 1] of Field -} -"; + acir(inline) fn main f0 { + b0(): + return [Field 1] of Field + } + "; assert_ssa_roundtrip(src); } #[test] fn test_return_empty_array() { let src = " -acir(inline) fn main f0 { - b0(): - return [] of Field -} -"; + acir(inline) fn main f0 { + b0(): + return [] of Field + } + "; assert_ssa_roundtrip(src); } #[test] fn test_return_composite_array() { let src = " -acir(inline) fn main f0 { - b0(): - return [Field 1, Field 2] of (Field, Field) -} -"; + acir(inline) fn main f0 { + b0(): + return [Field 1, Field 2] of (Field, Field) + } + "; assert_ssa_roundtrip(src); } #[test] fn test_block_parameters() { let src = " -acir(inline) fn main f0 { - b0(v0: Field, v1: Field): - return v0, v1 -} -"; + acir(inline) fn main f0 { + b0(v0: Field, v1: Field): + return v0, v1 + } + "; assert_ssa_roundtrip(src); } #[test] fn test_multiple_blocks_and_jmp() { let src = " -acir(inline) fn main f0 { - b0(): - jmp b1(Field 1) - b1(v1: Field): - return v1 -} -"; + acir(inline) fn main f0 { + b0(): + jmp b1(Field 1) + b1(v1: Field): + return v1 + } + "; assert_ssa_roundtrip(src); } #[test] fn test_jmpif() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - jmpif v0 then: b1, else: b2 - b1(): - return - b2(): - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + jmpif v0 then: b1, else: b2 + b1(): + return + b2(): + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_call() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - v2 = call f1(v0) -> Field - return v2 -} -acir(inline) fn foo f1 { - b0(v0: Field): - return v0 -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + v2 = call f1(v0) -> Field + return v2 + } + acir(inline) fn foo f1 { + b0(v0: Field): + return v0 + } + "; assert_ssa_roundtrip(src); } #[test] fn test_call_multiple_return_values() { let src = " -acir(inline) fn main f0 { - b0(): - v1, v2 = call f1() -> ([Field; 3], [Field; 1]) - return v1 -} -acir(inline) fn foo f1 { - b0(): - return [Field 1, Field 2, Field 3] of Field, [Field 4] of Field -} -"; + acir(inline) fn main f0 { + b0(): + v1, v2 = call f1() -> ([Field; 3], [Field; 1]) + return v1 + } + acir(inline) fn foo f1 { + b0(): + return [Field 1, Field 2, Field 3] of Field, [Field 4] of Field + } + "; assert_ssa_roundtrip(src); } #[test] fn test_call_no_return_value() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - call f1(v0) - return -} -acir(inline) fn foo f1 { - b0(v0: Field): - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + call f1(v0) + return + } + acir(inline) fn foo f1 { + b0(v0: Field): + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_call_intrinsic() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - call assert_constant(v0) - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + call assert_constant(v0) + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_cast() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - v1 = cast v0 as i32 - return v1 -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + v1 = cast v0 as i32 + return v1 + } + "; assert_ssa_roundtrip(src); } #[test] fn test_constrain() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - constrain v0 == Field 1 - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + constrain v0 == Field 1 + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_enable_side_effects() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - enable_side_effects v0 - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + enable_side_effects v0 + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_array_get() { let src = " -acir(inline) fn main f0 { - b0(v0: [Field; 3]): - v2 = array_get v0, index Field 0 -> Field - return -} -"; + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index Field 0 -> Field + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_array_set() { let src = " -acir(inline) fn main f0 { - b0(v0: [Field; 3]): - v3 = array_set v0, index Field 0, value Field 1 - return -} -"; + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v3 = array_set v0, index Field 0, value Field 1 + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_array_get_set_bug() { let src = " -acir(inline) fn main f0 { - b0(v0: [u32; 3]): - v3 = array_set v0, index u32 1, value u32 2 - v5 = array_get v3, index u32 0 -> u32 - return -} -"; + acir(inline) fn main f0 { + b0(v0: [u32; 3]): + v3 = array_set v0, index u32 1, value u32 2 + v5 = array_get v3, index u32 0 -> u32 + return + } + "; assert_ssa_roundtrip(src); } @@ -260,12 +260,12 @@ fn test_binary() { for op in ["add", "sub", "mul", "div", "eq", "mod", "lt", "and", "or", "xor", "shl", "shr"] { let src = format!( " -acir(inline) fn main f0 {{ - b0(v0: Field, v1: Field): - v2 = {op} v0, v1 - return -}} -" + acir(inline) fn main f0 {{ + b0(v0: Field, v1: Field): + v2 = {op} v0, v1 + return + }} + " ); assert_ssa_roundtrip(&src); } @@ -274,95 +274,95 @@ acir(inline) fn main f0 {{ #[test] fn test_truncate() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - v1 = truncate v0 to 8 bits, max_bit_size: 16 - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + v1 = truncate v0 to 8 bits, max_bit_size: 16 + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_not() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - v1 = not v0 - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + v1 = not v0 + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_range_check() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - range_check v0 to 8 bits - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + range_check v0 to 8 bits + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_allocate() { let src = " -acir(inline) fn main f0 { - b0(): - v0 = allocate -> [Field; 3] - return -} -"; + acir(inline) fn main f0 { + b0(): + v0 = allocate -> [Field; 3] + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_load() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - v1 = load v0 -> Field - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + v1 = load v0 -> Field + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_store() { let src = " -acir(inline) fn main f0 { - b0(v0: Field): - store Field 1 at v0 - return -} -"; + acir(inline) fn main f0 { + b0(v0: Field): + store Field 1 at v0 + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_inc_rc() { let src = " -acir(inline) fn main f0 { - b0(v0: [Field; 3]): - inc_rc v0 - return -} -"; + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + inc_rc v0 + return + } + "; assert_ssa_roundtrip(src); } #[test] fn test_dec_rc() { let src = " -acir(inline) fn main f0 { - b0(v0: [Field; 3]): - dec_rc v0 - return -} -"; + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + dec_rc v0 + return + } + "; assert_ssa_roundtrip(src); } From f720d393da77bd261ef22282c5c023d783850005 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 17:05:43 -0300 Subject: [PATCH 44/65] Update more examples --- .../src/ssa/opt/constant_folding.rs | 70 ++++++---------- .../src/ssa/opt/flatten_cfg.rs | 79 ++++++++----------- 2 files changed, 57 insertions(+), 92 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index e2c81cce7cd..c6296426012 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -321,7 +321,7 @@ mod test { instruction::{Binary, BinaryOp, Instruction, TerminatorInstruction}, map::Id, types::Type, - value::{Value, ValueId}, + value::Value, }, opt::assert_ssa_equals, Ssa, @@ -400,59 +400,41 @@ mod test { #[test] fn non_redundant_truncation() { - // fn main f0 { - // b0(v0: u16, v1: u16): - // v2 = div v0, v1 - // v3 = truncate v2 to 8 bits, max_bit_size: 16 - // return v3 - // } - // // After constructing this IR, we set the value of v1 to 2^8 - 1. // This should not result in the truncation being removed. - let main_id = Id::test_new(0); + let src = " + acir(inline) fn main f0 { + b0(v0: u16, v1: u16): + v2 = div v0, v1 + v3 = truncate v2 to 8 bits, max_bit_size: 16 + return v3 + } + "; + let mut ssa = Ssa::from_str(src).unwrap(); + let main = ssa.main_mut(); - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::unsigned(16)); - let v1 = builder.add_parameter(Type::unsigned(16)); + let instructions = main.dfg[main.entry_block()].instructions(); + assert_eq!(instructions.len(), 2); // The final return is not counted + + let v1 = main.parameters()[1]; // Note that this constant does not guarantee that `v0/constant < 2^8`. We must then truncate the result. let constant = 2_u128.pow(8) - 1; - let constant = builder.numeric_constant(constant, Type::field()); - - let v2 = builder.insert_binary(v0, BinaryOp::Div, v1); - let v3 = builder.insert_truncate(v2, 8, 16); - builder.terminate_with_return(vec![v3]); - - let mut ssa = builder.finish(); - let main = ssa.main_mut(); - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 2); // The final return is not counted + let constant = main.dfg.make_constant(constant.into(), Type::unsigned(16)); - // Expected output: - // - // fn main f0 { - // b0(v0: u16, Field 255: Field): - // v6 = div v0, Field 255 - // v7 = truncate v6 to 8 bits, max_bit_size: 16 - // return v7 - // } main.dfg.set_value_from_id(v1, constant); - let ssa = ssa.fold_constants(); - let main = ssa.main(); - - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 2); + let expected = " + acir(inline) fn main f0 { + b0(v0: u16, v1: u16): + v3 = div v0, u16 255 + v4 = truncate v3 to 8 bits, max_bit_size: 16 + return v4 + } + "; - assert_eq!( - &main.dfg[instructions[0]], - &Instruction::Binary(Binary { lhs: v0, operator: BinaryOp::Div, rhs: constant }) - ); - assert_eq!( - &main.dfg[instructions[1]], - &Instruction::Truncate { value: ValueId::test_new(6), bit_size: 8, max_bit_size: 16 } - ); + let ssa = ssa.fold_constants(); + assert_ssa_equals(ssa, expected); } #[test] diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 984f639df00..450c81fe61d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -930,61 +930,44 @@ mod test { types::Type, value::{Value, ValueId}, }, + opt::assert_ssa_equals, + Ssa, }; #[test] fn basic_jmpif() { - // fn main f0 { - // b0(v0: b1): - // jmpif v0, then: b1, else: b2 - // b1(): - // jmp b3(Field 3) - // b2(): - // jmp b3(Field 4) - // b3(v1: Field): - // return v1 - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id); - - let b1 = builder.insert_block(); - let b2 = builder.insert_block(); - let b3 = builder.insert_block(); - - let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_block_parameter(b3, Type::field()); - - let three = builder.field_constant(3u128); - let four = builder.field_constant(4u128); - - builder.terminate_with_jmpif(v0, b1, b2); - - builder.switch_to_block(b1); - builder.terminate_with_jmp(b3, vec![three]); - - builder.switch_to_block(b2); - builder.terminate_with_jmp(b3, vec![four]); - - builder.switch_to_block(b3); - builder.terminate_with_return(vec![v1]); - - let ssa = builder.finish(); + let src = " + acir(inline) fn main f0 { + b0(v0: u1): + jmpif v0 then: b1, else: b2 + b1(): + jmp b3(Field 3) + b3(v1: Field): + return v1 + b2(): + jmp b3(Field 4) + } + "; + let ssa = Ssa::from_str(src).unwrap(); assert_eq!(ssa.main().reachable_blocks().len(), 4); - // Expected output: - // fn main f0 { - // b0(v0: u1): - // enable_side_effects v0 - // v5 = not v0 - // enable_side_effects v5 - // enable_side_effects u1 1 - // v7 = mul v0, Field 3 - // v8 = mul v5, Field 4 - // v9 = add v7, v8 - // return v9 - // } + let expected = " + acir(inline) fn main f0 { + b0(v0: u1): + enable_side_effects v0 + v1 = not v0 + enable_side_effects u1 1 + v3 = cast v0 as Field + v4 = cast v1 as Field + v6 = mul v3, Field 3 + v8 = mul v4, Field 4 + v9 = add v6, v8 + return v9 + } + "; + let ssa = ssa.flatten_cfg(); - assert_eq!(ssa.main().reachable_blocks().len(), 1); + assert_ssa_equals(ssa, expected); } #[test] From 08e8f3350ee944c29559392b2bdbc7b494d6dfe8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 17:11:24 -0300 Subject: [PATCH 45/65] Fix test --- .../src/ssa/opt/constant_folding.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index c6296426012..27ef2e669bf 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -663,19 +663,19 @@ mod test { acir(inline) fn main f0 { b0(v0: u1, v1: u1, v2: [Field; 2]): enable_side_effects v0 - v6 = array_get u32, v2, index u32 0 + v6 = array_get v2, index u32 0 -> u32 v7 = array_set v2, index u32 1, value u32 2 - v8 = array_get u32, v7, index u32 0 + v8 = array_get v7, index u32 0 -> u32 constrain v6 == v8 enable_side_effects v1 - v9 = array_get u32, v2, index u32 0 + v9 = array_get v2, index u32 0 -> u32 v10 = array_set v2, index u32 1, value u32 2 - v11 = array_get u32, v10, index u32 0 + v11 = array_get v10, index u32 0 -> u32 constrain v9 == v11 enable_side_effects v0 - v12 = array_get u32, v2, index u32 0 + v12 = array_get v2, index u32 0 -> u32 v13 = array_set v2, index u32 1, value u32 2 - v14 = array_get u32, v13, index u32 0 + v14 = array_get v13, index u32 0 -> u32 constrain v12 == v14 return } @@ -690,13 +690,13 @@ acir(inline) fn main f0 { acir(inline) fn main f0 { b0(v0: u1, v1: u1, v2: [Field; 2]): enable_side_effects v0 - v4 = array_get u32, v2, index u32 0 + v4 = array_get v2, index u32 0 -> u32 v7 = array_set v2, index u32 1, value u32 2 - v8 = array_get u32, v7, index u32 0 + v8 = array_get v7, index u32 0 -> u32 constrain v4 == v8 enable_side_effects v1 v9 = array_set v2, index u32 1, value u32 2 - v10 = array_get u32, v9, index u32 0 + v10 = array_get v9, index u32 0 -> u32 constrain v4 == v10 enable_side_effects v0 return From 2e13c5966899ddd13bb8699dc06dbdb0b4792ca3 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 17:21:32 -0300 Subject: [PATCH 46/65] Another example --- .../noirc_evaluator/src/ssa/opt/array_set.rs | 154 ++++-------------- 1 file changed, 33 insertions(+), 121 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index e29bcaf0e7a..869c5fdb1a6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -206,135 +206,47 @@ fn make_mutable( #[cfg(test)] mod tests { - use std::sync::Arc; - - use im::vector; - use noirc_frontend::monomorphization::ast::InlineType; - - use crate::ssa::{ - function_builder::FunctionBuilder, - ir::{ - function::RuntimeType, - instruction::{BinaryOp, Instruction}, - map::Id, - types::Type, - }, - }; + use crate::ssa::{opt::assert_ssa_equals, Ssa}; #[test] fn array_set_in_loop_with_conditional_clone() { // We want to make sure that we do not mark a single array set mutable which is loaded // from and cloned in a loop. If the array is inadvertently marked mutable, and is cloned in a previous iteration // of the loop, its clone will also be altered. - // - // brillig fn main f0 { - // b0(): - // v3 = allocate - // store [[Field 0, Field 0, Field 0, Field 0, Field 0], [Field 0, Field 0, Field 0, Field 0, Field 0]] at v3 - // v4 = allocate - // store [[Field 0, Field 0, Field 0, Field 0, Field 0], [Field 0, Field 0, Field 0, Field 0, Field 0]] at v4 - // jmp b1(u32 0) - // b1(v6: u32): - // v8 = lt v6, u32 5 - // jmpif v8 then: b3, else: b2 - // b3(): - // v9 = eq v6, u32 5 - // jmpif v9 then: b4, else: b5 - // b4(): - // v10 = load v3 - // store v10 at v4 - // jmp b5() - // b5(): - // v11 = load v3 - // v13 = array_get v11, index Field 0 - // v14 = array_set v13, index v6, value Field 20 - // v15 = array_set v11, index v6, value v14 - // store v15 at v3 - // v17 = add v6, u32 1 - // jmp b1(v17) - // b2(): - // return - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id); - builder.set_runtime(RuntimeType::Brillig(InlineType::default())); - - let array_type = Type::Array(Arc::new(vec![Type::field()]), 5); - let zero = builder.field_constant(0u128); - let array_constant = - builder.array_constant(vector![zero, zero, zero, zero, zero], array_type.clone()); - let nested_array_type = Type::Array(Arc::new(vec![array_type.clone()]), 2); - let nested_array_constant = builder - .array_constant(vector![array_constant, array_constant], nested_array_type.clone()); - - let v3 = builder.insert_allocate(array_type.clone()); - - builder.insert_store(v3, nested_array_constant); - - let v4 = builder.insert_allocate(array_type.clone()); - builder.insert_store(v4, nested_array_constant); - - let b1 = builder.insert_block(); - let zero_u32 = builder.numeric_constant(0u128, Type::unsigned(32)); - builder.terminate_with_jmp(b1, vec![zero_u32]); - - // Loop header - builder.switch_to_block(b1); - let v5 = builder.add_block_parameter(b1, Type::unsigned(32)); - let five = builder.numeric_constant(5u128, Type::unsigned(32)); - let v8 = builder.insert_binary(v5, BinaryOp::Lt, five); - - let b2 = builder.insert_block(); - let b3 = builder.insert_block(); - let b4 = builder.insert_block(); - let b5 = builder.insert_block(); - builder.terminate_with_jmpif(v8, b3, b2); - - // Loop body - // b3 is the if statement conditional - builder.switch_to_block(b3); - let two = builder.numeric_constant(5u128, Type::unsigned(32)); - let v9 = builder.insert_binary(v5, BinaryOp::Eq, two); - builder.terminate_with_jmpif(v9, b4, b5); - - // b4 is the rest of the loop after the if statement - builder.switch_to_block(b4); - let v10 = builder.insert_load(v3, nested_array_type.clone()); - builder.insert_store(v4, v10); - builder.terminate_with_jmp(b5, vec![]); - - builder.switch_to_block(b5); - let v11 = builder.insert_load(v3, nested_array_type.clone()); - let twenty = builder.field_constant(20u128); - let v13 = builder.insert_array_get(v11, zero, array_type.clone()); - let v14 = builder.insert_array_set(v13, v5, twenty); - let v15 = builder.insert_array_set(v11, v5, v14); - - builder.insert_store(v3, v15); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); - let v17 = builder.insert_binary(v5, BinaryOp::Add, one); - builder.terminate_with_jmp(b1, vec![v17]); - - builder.switch_to_block(b2); - builder.terminate_with_return(vec![]); + let src = " + brillig(inline) fn main f0 { + b0(): + v1 = allocate -> [Field; 5] + store [[Field 0, Field 0, Field 0, Field 0, Field 0] of Field, [Field 0, Field 0, Field 0, Field 0, Field 0] of Field] of [Field; 5] at v1 + v6 = allocate -> [Field; 5] + store [[Field 0, Field 0, Field 0, Field 0, Field 0] of Field, [Field 0, Field 0, Field 0, Field 0, Field 0] of Field] of [Field; 5] at v6 + jmp b1(u32 0) + b1(v0: u32): + v12 = lt v0, u32 5 + jmpif v12 then: b3, else: b2 + b3(): + v13 = eq v0, u32 5 + jmpif v13 then: b4, else: b5 + b4(): + v14 = load v1 -> [[Field; 5]; 2] + store v14 at v6 + jmp b5() + b5(): + v15 = load v1 -> [[Field; 5]; 2] + v16 = array_get v15, index Field 0 -> [Field; 5] + v18 = array_set v16, index v0, value Field 20 + v19 = array_set v15, index v0, value v18 + store v19 at v1 + v21 = add v0, u32 1 + jmp b1(v21) + b2(): + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); - let ssa = builder.finish(); // We expect the same result as above let ssa = ssa.array_set_optimization(); - - let main = ssa.main(); - assert_eq!(main.reachable_blocks().len(), 6); - - let array_set_instructions = main.dfg[b5] - .instructions() - .iter() - .filter(|instruction| matches!(&main.dfg[**instruction], Instruction::ArraySet { .. })) - .collect::>(); - - assert_eq!(array_set_instructions.len(), 2); - if let Instruction::ArraySet { mutable, .. } = &main.dfg[*array_set_instructions[0]] { - // The single array set should not be marked mutable - assert!(!mutable); - } + assert_ssa_equals(ssa, src); } } From 53d3027950d5f13386d9ca95c05f956ad258421d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 17:22:57 -0300 Subject: [PATCH 47/65] Align src --- .../src/ssa/opt/constant_folding.rs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 27ef2e669bf..17e371538e6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -660,26 +660,26 @@ mod test { #[test] fn deduplicate_instructions_with_predicates() { let src = " -acir(inline) fn main f0 { - b0(v0: u1, v1: u1, v2: [Field; 2]): - enable_side_effects v0 - v6 = array_get v2, index u32 0 -> u32 - v7 = array_set v2, index u32 1, value u32 2 - v8 = array_get v7, index u32 0 -> u32 - constrain v6 == v8 - enable_side_effects v1 - v9 = array_get v2, index u32 0 -> u32 - v10 = array_set v2, index u32 1, value u32 2 - v11 = array_get v10, index u32 0 -> u32 - constrain v9 == v11 - enable_side_effects v0 - v12 = array_get v2, index u32 0 -> u32 - v13 = array_set v2, index u32 1, value u32 2 - v14 = array_get v13, index u32 0 -> u32 - constrain v12 == v14 - return -} - "; + acir(inline) fn main f0 { + b0(v0: u1, v1: u1, v2: [Field; 2]): + enable_side_effects v0 + v6 = array_get v2, index u32 0 -> u32 + v7 = array_set v2, index u32 1, value u32 2 + v8 = array_get v7, index u32 0 -> u32 + constrain v6 == v8 + enable_side_effects v1 + v9 = array_get v2, index u32 0 -> u32 + v10 = array_set v2, index u32 1, value u32 2 + v11 = array_get v10, index u32 0 -> u32 + constrain v9 == v11 + enable_side_effects v0 + v12 = array_get v2, index u32 0 -> u32 + v13 = array_set v2, index u32 1, value u32 2 + v14 = array_get v13, index u32 0 -> u32 + constrain v12 == v14 + return + } + "; let ssa = Ssa::from_str(src).unwrap(); let main = ssa.main(); @@ -687,21 +687,21 @@ acir(inline) fn main f0 { assert_eq!(instructions.len(), 15); let expected = " -acir(inline) fn main f0 { - b0(v0: u1, v1: u1, v2: [Field; 2]): - enable_side_effects v0 - v4 = array_get v2, index u32 0 -> u32 - v7 = array_set v2, index u32 1, value u32 2 - v8 = array_get v7, index u32 0 -> u32 - constrain v4 == v8 - enable_side_effects v1 - v9 = array_set v2, index u32 1, value u32 2 - v10 = array_get v9, index u32 0 -> u32 - constrain v4 == v10 - enable_side_effects v0 - return -} - "; + acir(inline) fn main f0 { + b0(v0: u1, v1: u1, v2: [Field; 2]): + enable_side_effects v0 + v4 = array_get v2, index u32 0 -> u32 + v7 = array_set v2, index u32 1, value u32 2 + v8 = array_get v7, index u32 0 -> u32 + constrain v4 == v8 + enable_side_effects v1 + v9 = array_set v2, index u32 1, value u32 2 + v10 = array_get v9, index u32 0 -> u32 + constrain v4 == v10 + enable_side_effects v0 + return + } + "; let ssa = ssa.fold_constants_using_constraints(); assert_ssa_equals(ssa, expected); From 457a20ba09c73d89fcb63f11ddb34f8129362f72 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 19:38:18 -0300 Subject: [PATCH 48/65] More examples --- .../src/ssa/opt/constant_folding.rs | 188 +++++------------- 1 file changed, 50 insertions(+), 138 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 17e371538e6..8daf6dee6ff 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -317,12 +317,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{ - instruction::{Binary, BinaryOp, Instruction, TerminatorInstruction}, - map::Id, - types::Type, - value::Value, - }, + ir::{map::Id, types::Type}, opt::assert_ssa_equals, Ssa, }; @@ -439,45 +434,18 @@ mod test { #[test] fn arrays_elements_are_updated() { - // fn main f0 { - // b0(v0: Field): - // v1 = add v0, Field 1 - // return [v1] - // } - // // After constructing this IR, we run constant folding with no expected benefit, but to // ensure that all new values ids are correctly propagated. - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::field()); - let one = builder.field_constant(1u128); - let v1 = builder.insert_binary(v0, BinaryOp::Add, one); - - let array_type = Type::Array(Arc::new(vec![Type::field()]), 1); - let arr = builder.current_function.dfg.make_array(vec![v1].into(), array_type); - builder.terminate_with_return(vec![arr]); - - let ssa = builder.finish().fold_constants(); - let main = ssa.main(); - let entry_block_id = main.entry_block(); - let entry_block = &main.dfg[entry_block_id]; - assert_eq!(entry_block.instructions().len(), 1); - let new_add_instr = entry_block.instructions().first().unwrap(); - let new_add_instr_result = main.dfg.instruction_results(*new_add_instr)[0]; - assert_ne!(new_add_instr_result, v1); - - let return_value_id = match entry_block.unwrap_terminator() { - TerminatorInstruction::Return { return_values, .. } => return_values[0], - _ => unreachable!("Should have terminator instruction"), - }; - let return_element = match &main.dfg[return_value_id] { - Value::Array { array, .. } => array[0], - _ => unreachable!("Return type should be array"), - }; - // The return element is expected to refer to the new add instruction result. - assert_eq!(main.dfg.resolve(new_add_instr_result), main.dfg.resolve(return_element)); + let src = " + acir(inline) fn main f0 { + b0(v0: Field): + v2 = add v0, Field 1 + return [v2] of Field + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.fold_constants(); + assert_ssa_equals(ssa, src); } #[test] @@ -545,116 +513,59 @@ mod test { #[test] fn constraint_decomposition() { - // fn main f0 { - // b0(v0: u1, v1: u1, v2: u1): - // v3 = mul v0 v1 - // v4 = not v2 - // v5 = mul v3 v4 - // constrain v4 u1 1 - // } - // // When constructing this IR, we should automatically decompose the constraint to be in terms of `v0`, `v1` and `v2`. // // The mul instructions are retained and will be removed in the dead instruction elimination pass. - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::bool()); - let v2 = builder.add_parameter(Type::bool()); - - let v3 = builder.insert_binary(v0, BinaryOp::Mul, v1); - let v4 = builder.insert_not(v2); - let v5 = builder.insert_binary(v3, BinaryOp::Mul, v4); - - // This constraint is automatically decomposed when it is inserted. - let v_true = builder.numeric_constant(true, Type::bool()); - builder.insert_constrain(v5, v_true, None); - - let v_false = builder.numeric_constant(false, Type::bool()); - - // Expected output: - // - // fn main f0 { - // b0(v0: u1, v1: u1, v2: u1): - // v3 = mul v0 v1 - // v4 = not v2 - // v5 = mul v3 v4 - // constrain v0 u1 1 - // constrain v1 u1 1 - // constrain v2 u1 0 - // } - - let ssa = builder.finish(); - let main = ssa.main(); - let instructions = main.dfg[main.entry_block()].instructions(); - - assert_eq!(instructions.len(), 6); + let src = " + acir(inline) fn main f0 { + b0(v0: u1, v1: u1, v2: u1): + v3 = mul v0, v1 + v4 = not v2 + v5 = mul v3, v4 + constrain v5 == u1 1 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); - assert_eq!( - main.dfg[instructions[0]], - Instruction::Binary(Binary { lhs: v0, operator: BinaryOp::Mul, rhs: v1 }) - ); - assert_eq!(main.dfg[instructions[1]], Instruction::Not(v2)); - assert_eq!( - main.dfg[instructions[2]], - Instruction::Binary(Binary { lhs: v3, operator: BinaryOp::Mul, rhs: v4 }) - ); - assert_eq!(main.dfg[instructions[3]], Instruction::Constrain(v0, v_true, None)); - assert_eq!(main.dfg[instructions[4]], Instruction::Constrain(v1, v_true, None)); - assert_eq!(main.dfg[instructions[5]], Instruction::Constrain(v2, v_false, None)); + let expected = " + acir(inline) fn main f0 { + b0(v0: u1, v1: u1, v2: u1): + v3 = mul v0, v1 + v4 = not v2 + v5 = mul v3, v4 + constrain v0 == u1 1 + constrain v1 == u1 1 + constrain v2 == u1 0 + return + } + "; + assert_ssa_equals(ssa, expected); } // Regression for #4600 #[test] fn array_get_regression() { - // fn main f0 { - // b0(v0: u1, v1: u64): - // enable_side_effects_if v0 - // v2 = array_get [Field 0, Field 1], index v1 - // v3 = not v0 - // enable_side_effects_if v3 - // v4 = array_get [Field 0, Field 1], index v1 - // } - // // We want to make sure after constant folding both array_gets remain since they are // under different enable_side_effects_if contexts and thus one may be disabled while // the other is not. If one is removed, it is possible e.g. v4 is replaced with v2 which // is disabled (only gets from index 0) and thus returns the wrong result. - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::unsigned(64)); - - builder.insert_enable_side_effects_if(v0); - - let zero = builder.field_constant(0u128); - let one = builder.field_constant(1u128); - - let typ = Type::Array(Arc::new(vec![Type::field()]), 2); - let array = builder.array_constant(vec![zero, one].into(), typ); - - let _v2 = builder.insert_array_get(array, v1, Type::field()); - let v3 = builder.insert_not(v0); - - builder.insert_enable_side_effects_if(v3); - let _v4 = builder.insert_array_get(array, v1, Type::field()); + let src = " + acir(inline) fn main f0 { + b0(v0: u1, v1: u64): + enable_side_effects v0 + v5 = array_get [Field 0, Field 1] of Field, index v1 -> Field + v6 = not v0 + enable_side_effects v6 + v8 = array_get [Field 0, Field 1] of Field, index v1 -> Field + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); // Expected output is unchanged - let ssa = builder.finish(); - let main = ssa.main(); - let instructions = main.dfg[main.entry_block()].instructions(); - let starting_instruction_count = instructions.len(); - assert_eq!(starting_instruction_count, 5); - let ssa = ssa.fold_constants(); - let main = ssa.main(); - let instructions = main.dfg[main.entry_block()].instructions(); - let ending_instruction_count = instructions.len(); - assert_eq!(starting_instruction_count, ending_instruction_count); + assert_ssa_equals(ssa, src); } #[test] @@ -741,7 +652,8 @@ mod test { let _v10 = builder.insert_call(keccakf1600, vec![array1], vec![typ.clone()]); let _v11 = builder.insert_call(keccakf1600, vec![array2], vec![typ.clone()]); - let ssa = builder.finish(); + let mut ssa = builder.finish(); + ssa.normalize_ids(); println!("{ssa}"); From f75ca44d93e61f0ec437c64f7eca11ecbceea088 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 19:43:16 -0300 Subject: [PATCH 49/65] mutable array_set --- .../noirc_evaluator/src/ssa/function_builder/mod.rs | 10 ++++++++++ compiler/noirc_evaluator/src/ssa/parser.rs | 9 ++++++++- compiler/noirc_evaluator/src/ssa/parser/ast.rs | 1 + compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs | 8 ++++++-- compiler/noirc_evaluator/src/ssa/parser/tests.rs | 12 ++++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index aebb47ccf8e..61284b0be95 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -327,6 +327,16 @@ impl FunctionBuilder { .first() } + pub(crate) fn insert_mutable_array_set( + &mut self, + array: ValueId, + index: ValueId, + value: ValueId, + ) -> ValueId { + self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: true }, None) + .first() + } + /// Insert an instruction to increment an array's reference count. This only has an effect /// in unconstrained code where arrays are reference counted and copy on write. pub(crate) fn insert_inc_rc(&mut self, value: ValueId) { diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index ced2e3c6a5b..827b5893d89 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -244,6 +244,7 @@ impl<'a> Parser<'a> { } if self.eat_keyword(Keyword::ArraySet)? { + let mutable = self.eat_keyword(Keyword::Mut)?; let array = self.parse_value_or_error()?; self.eat_or_error(Token::Comma)?; self.eat_or_error(Token::Keyword(Keyword::Index))?; @@ -251,7 +252,13 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::Comma)?; self.eat_or_error(Token::Keyword(Keyword::Value))?; let value = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::ArraySet { target, array, index, value })); + return Ok(Some(ParsedInstruction::ArraySet { + target, + array, + index, + value, + mutable, + })); } if self.eat_keyword(Keyword::Cast)? { diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index df3ddefc8a6..7fecf05e19f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -59,6 +59,7 @@ pub(crate) enum ParsedInstruction { array: ParsedValue, index: ParsedValue, value: ParsedValue, + mutable: bool, }, BinaryOp { target: Identifier, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 43c33de9c08..15fed18f546 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -143,11 +143,15 @@ impl Translator { let value_id = self.builder.insert_array_get(array, index, element_type); self.define_variable(target, value_id)?; } - ParsedInstruction::ArraySet { target, array, index, value } => { + ParsedInstruction::ArraySet { target, array, index, value, mutable } => { let array = self.translate_value(array)?; let index = self.translate_value(index)?; let value = self.translate_value(value)?; - let value_id = self.builder.insert_array_set(array, index, value); + let value_id = if mutable { + self.builder.insert_mutable_array_set(array, index, value) + } else { + self.builder.insert_array_set(array, index, value) + }; self.define_variable(target, value_id)?; } ParsedInstruction::BinaryOp { target, lhs, op, rhs } => { diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 061110130e1..c48e3c7340b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -242,6 +242,18 @@ fn test_array_set() { assert_ssa_roundtrip(src); } +#[test] +fn test_mutable_array_set() { + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v3 = array_set mut v0, index Field 0, value Field 1 + return + } + "; + assert_ssa_roundtrip(src); +} + #[test] fn test_array_get_set_bug() { let src = " diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 9b6e2240770..9faea39778e 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -101,6 +101,7 @@ pub(crate) enum Keyword { MaxBitSize, Mod, Mul, + Mut, NoPredicates, Not, Of, @@ -154,6 +155,7 @@ impl Keyword { "max_bit_size" => Keyword::MaxBitSize, "mod" => Keyword::Mod, "mul" => Keyword::Mul, + "mut" => Keyword::Mut, "no_predicates" => Keyword::NoPredicates, "not" => Keyword::Not, "of" => Keyword::Of, From a049769179231de104362c7f2b526685fabe4d78 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 10 Nov 2024 20:00:17 -0300 Subject: [PATCH 50/65] Some die tests --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 235 +++++++------------- 1 file changed, 79 insertions(+), 156 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index beca7c41e5c..0c33085cd04 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -622,177 +622,100 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{ - instruction::{BinaryOp, Instruction, Intrinsic}, - map::Id, - types::Type, - }, + ir::{instruction::Instruction, map::Id, types::Type}, + opt::assert_ssa_equals, + Ssa, }; #[test] fn dead_instruction_elimination() { - // fn main f0 { - // b0(v0: Field): - // v1 = add v0, Field 1 - // v2 = add v0, Field 2 - // jmp b1(v2) - // b1(v3: Field): - // v4 = allocate 1 field - // v5 = load v4 - // v6 = allocate 1 field - // store Field 1 in v6 - // v7 = load v6 - // v8 = add v7, Field 1 - // v9 = add v7, Field 2 - // v10 = add v7, Field 3 - // v11 = add v10, v10 - // call assert_constant(v8) - // return v9 - // } - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::field()); - let b1 = builder.insert_block(); - - let one = builder.field_constant(1u128); - let two = builder.field_constant(2u128); - let three = builder.field_constant(3u128); - - let _v1 = builder.insert_binary(v0, BinaryOp::Add, one); - let v2 = builder.insert_binary(v0, BinaryOp::Add, two); - builder.terminate_with_jmp(b1, vec![v2]); - - builder.switch_to_block(b1); - let _v3 = builder.add_block_parameter(b1, Type::field()); - - let v4 = builder.insert_allocate(Type::field()); - let _v5 = builder.insert_load(v4, Type::field()); - - let v6 = builder.insert_allocate(Type::field()); - builder.insert_store(v6, one); - let v7 = builder.insert_load(v6, Type::field()); - let v8 = builder.insert_binary(v7, BinaryOp::Add, one); - let v9 = builder.insert_binary(v7, BinaryOp::Add, two); - let v10 = builder.insert_binary(v7, BinaryOp::Add, three); - let _v11 = builder.insert_binary(v10, BinaryOp::Add, v10); - - let assert_constant_id = builder.import_intrinsic_id(Intrinsic::AssertConstant); - builder.insert_call(assert_constant_id, vec![v8], vec![]); - builder.terminate_with_return(vec![v9]); - - let ssa = builder.finish(); - let main = ssa.main(); - - // The instruction count never includes the terminator instruction - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 2); - assert_eq!(main.dfg[b1].instructions().len(), 10); - - // Expected output: - // - // fn main f0 { - // b0(v0: Field): - // v2 = add v0, Field 2 - // jmp b1(v2) - // b1(v3: Field): - // v6 = allocate 1 field - // store Field 1 in v6 - // v7 = load v6 - // v8 = add v7, Field 1 - // v9 = add v7, Field 2 - // call assert_constant(v8) - // return v9 - // } + let src = " + acir(inline) fn main f0 { + b0(v0: Field): + v3 = add v0, Field 1 + v5 = add v0, Field 2 + jmp b1(v5) + b1(v1: Field): + v6 = allocate -> Field + v7 = load v6 -> Field + v8 = allocate -> Field + store Field 1 at v8 + v9 = load v8 -> Field + v10 = add v9, Field 1 + v11 = add v9, Field 2 + v13 = add v9, Field 3 + v14 = add v13, v13 + call assert_constant(v10) + return v11 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: Field): + v3 = add v0, Field 2 + jmp b1(v3) + b1(v1: Field): + v4 = allocate -> Field + store Field 1 at v4 + v6 = load v4 -> Field + v7 = add v6, Field 1 + v8 = add v6, Field 2 + call assert_constant(v7) + return v8 + } + "; let ssa = ssa.dead_instruction_elimination(); - let main = ssa.main(); - - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 1); - assert_eq!(main.dfg[b1].instructions().len(), 6); + assert_ssa_equals(ssa, expected); } #[test] fn as_witness_die() { - // fn main f0 { - // b0(v0: Field): - // v1 = add v0, Field 1 - // v2 = add v0, Field 2 - // call as_witness(v2) - // return v1 - // } - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::field()); - - let one = builder.field_constant(1u128); - let two = builder.field_constant(2u128); - - let v1 = builder.insert_binary(v0, BinaryOp::Add, one); - let v2 = builder.insert_binary(v0, BinaryOp::Add, two); - let as_witness = builder.import_intrinsic("as_witness").unwrap(); - builder.insert_call(as_witness, vec![v2], Vec::new()); - builder.terminate_with_return(vec![v1]); - - let ssa = builder.finish(); - let main = ssa.main(); - - // The instruction count never includes the terminator instruction - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); - - // Expected output: - // - // acir(inline) fn main f0 { - // b0(v0: Field): - // v3 = add v0, Field 1 - // return v3 - // } + let src = " + acir(inline) fn main f0 { + b0(v0: Field): + v2 = add v0, Field 1 + v4 = add v0, Field 2 + call as_witness(v4) + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: Field): + v2 = add v0, Field 1 + return v2 + } + "; let ssa = ssa.dead_instruction_elimination(); - let main = ssa.main(); - - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 1); + assert_ssa_equals(ssa, expected); } #[test] fn remove_useless_paired_rcs_even_when_used() { - // acir(inline) fn main f0 { - // b0(v0: [Field; 2]): - // inc_rc v0 - // v2 = array_get v0, index u32 0 - // dec_rc v0 - // return v2 - // } - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 2)); - builder.increment_array_reference_count(v0); - let zero = builder.numeric_constant(0u128, Type::unsigned(32)); - let v1 = builder.insert_array_get(v0, zero, Type::field()); - builder.decrement_array_reference_count(v0); - builder.terminate_with_return(vec![v1]); - - let ssa = builder.finish(); - let main = ssa.main(); - - // The instruction count never includes the terminator instruction - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); - - // Expected output: - // - // acir(inline) fn main f0 { - // b0(v0: [Field; 2]): - // v2 = array_get v0, index u32 0 - // return v2 - // } + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 2]): + inc_rc v0 + v2 = array_get v0, index u32 0 -> Field + dec_rc v0 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: [Field; 2]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; let ssa = ssa.dead_instruction_elimination(); - let main = ssa.main(); - - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 1); - assert!(matches!(&main.dfg[instructions[0]], Instruction::ArrayGet { .. })); + assert_ssa_equals(ssa, expected); } #[test] From 9bb740fd9a57b096ac6871571bfe5c77e578badd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 07:28:30 -0300 Subject: [PATCH 51/65] More die test s --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 93 +++++++-------------- 1 file changed, 31 insertions(+), 62 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 0c33085cd04..37a93cf0f40 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -720,35 +720,20 @@ mod test { #[test] fn keep_paired_rcs_with_array_set() { - // acir(inline) fn main f0 { - // b0(v0: [Field; 2]): - // inc_rc v0 - // v2 = array_set v0, index u32 0, value u32 0 - // dec_rc v0 - // return v2 - // } - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 2)); - builder.increment_array_reference_count(v0); - let zero = builder.numeric_constant(0u128, Type::unsigned(32)); - let v1 = builder.insert_array_set(v0, zero, zero); - builder.decrement_array_reference_count(v0); - builder.terminate_with_return(vec![v1]); - - let ssa = builder.finish(); - let main = ssa.main(); - - // The instruction count never includes the terminator instruction - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 2]): + inc_rc v0 + v2 = array_set v0, index u32 0, value u32 0 + dec_rc v0 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); // We expect the output to be unchanged let ssa = ssa.dead_instruction_elimination(); - let main = ssa.main(); - - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); + assert_ssa_equals(ssa, src); } #[test] @@ -863,47 +848,31 @@ mod test { #[test] fn remove_inc_rcs_that_are_never_mutably_borrowed() { - // acir(inline) fn main f0 { - // b0(v0: [Field; 2]): - // inc_rc v0 - // inc_rc v0 - // inc_rc v0 - // v2 = array_get v0, index u32 0 - // inc_rc v0 - // return v2 - // } - let main_id = Id::test_new(0); - - // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id); - let v0 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 2)); - builder.increment_array_reference_count(v0); - builder.increment_array_reference_count(v0); - builder.increment_array_reference_count(v0); - - let zero = builder.numeric_constant(0u128, Type::unsigned(32)); - let v2 = builder.insert_array_get(v0, zero, Type::field()); - builder.increment_array_reference_count(v0); - builder.terminate_with_return(vec![v2]); - - let ssa = builder.finish(); + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 2]): + inc_rc v0 + inc_rc v0 + inc_rc v0 + v2 = array_get v0, index u32 0 -> Field + inc_rc v0 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); let main = ssa.main(); // The instruction count never includes the terminator instruction assert_eq!(main.dfg[main.entry_block()].instructions().len(), 5); - // Expected output: - // - // acir(inline) fn main f0 { - // b0(v0: [Field; 2]): - // v2 = array_get v0, index u32 0 - // return v2 - // } + let expected = " + acir(inline) fn main f0 { + b0(v0: [Field; 2]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; let ssa = ssa.dead_instruction_elimination(); - let main = ssa.main(); - - let instructions = main.dfg[main.entry_block()].instructions(); - assert_eq!(instructions.len(), 1); - assert!(matches!(&main.dfg[instructions[0]], Instruction::ArrayGet { .. })); + assert_ssa_equals(ssa, expected); } } From 9415de2453ee9e24b6a23c0d87cc412da73a324d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 08:22:09 -0300 Subject: [PATCH 52/65] Parse &mut type --- compiler/noirc_evaluator/src/ssa/parser.rs | 6 ++++++ compiler/noirc_evaluator/src/ssa/parser/lexer.rs | 1 + compiler/noirc_evaluator/src/ssa/parser/tests.rs | 11 +++++++++++ compiler/noirc_evaluator/src/ssa/parser/token.rs | 2 ++ 4 files changed, 20 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 827b5893d89..dd6500366e0 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -630,6 +630,12 @@ impl<'a> Parser<'a> { return Ok(Type::Array(Arc::new(element_types), length.to_u128() as usize)); } + if self.eat(Token::Ampersand)? { + self.eat_or_error(Token::Keyword(Keyword::Mut))?; + let typ = self.parse_type()?; + return Ok(Type::Reference(Arc::new(typ))); + } + self.expected_type() } diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index a4e9941f3fe..e807c2c0a8d 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -49,6 +49,7 @@ impl<'a> Lexer<'a> { Some('}') => self.single_char_token(Token::RightBrace), Some('[') => self.single_char_token(Token::LeftBracket), Some(']') => self.single_char_token(Token::RightBracket), + Some('&') => self.single_char_token(Token::Ampersand), Some('-') if self.peek_char() == Some('>') => self.double_char_token(Token::Arrow), Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch), Some(char) => Err(LexerError::UnexpectedCharacter { diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index c48e3c7340b..0c553e7c145 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -378,3 +378,14 @@ fn test_dec_rc() { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_mutable_reference_type() { + let src = " + acir(inline) fn main f0 { + b0(v0: &mut Field): + return + } + "; + assert_ssa_roundtrip(src); +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 9faea39778e..b9cf94139da 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -53,6 +53,8 @@ pub(crate) enum Token { Arrow, /// == Equal, + /// & + Ampersand, Eof, } From 0254be9075ef09eeec840e41857110c44a1be13a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 08:42:05 -0300 Subject: [PATCH 53/65] A few more tests --- .../src/ssa/opt/flatten_cfg.rs | 248 +++++++----------- 1 file changed, 92 insertions(+), 156 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 450c81fe61d..3d9a9af56e4 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -972,178 +972,114 @@ mod test { #[test] fn modify_constrain() { - // fn main f0 { - // b0(v0: u1, v1: u1): - // jmpif v0, then: b1, else: b2 - // b1(): - // constrain v1 - // jmp b2() - // b2(): - // return - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id); - - let b1 = builder.insert_block(); - let b2 = builder.insert_block(); - - let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::bool()); - let v_true = builder.numeric_constant(true, Type::bool()); - - builder.terminate_with_jmpif(v0, b1, b2); - - builder.switch_to_block(b1); - builder.insert_constrain(v1, v_true, None); - builder.terminate_with_jmp(b2, vec![]); - - builder.switch_to_block(b2); - builder.terminate_with_return(vec![]); - - let ssa = builder.finish(); + let src = " + acir(inline) fn main f0 { + b0(v0: u1, v1: u1): + jmpif v0 then: b1, else: b2 + b1(): + constrain v1 == u1 1 + jmp b2() + b2(): + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); assert_eq!(ssa.main().reachable_blocks().len(), 3); - // Expected output: - // fn main f0 { - // b0(v0: u1, v1: u1): - // enable_side_effects v0 - // v3 = mul v1, v0 - // v4 = eq v3, v0 - // constrain v4 - // v5 = not v0 - // enable_side_effects v5 - // enable_side_effects u1 1 - // return - // } + let expected = " + acir(inline) fn main f0 { + b0(v0: u1, v1: u1): + enable_side_effects v0 + v2 = mul v1, v0 + constrain v2 == v0 + v3 = not v0 + enable_side_effects u1 1 + return + } + "; let ssa = ssa.flatten_cfg(); assert_eq!(ssa.main().reachable_blocks().len(), 1); + assert_ssa_equals(ssa, expected); } #[test] fn merge_stores() { - // fn main f0 { - // b0(v0: u1, v1: &mut Field): - // jmpif v0, then: b1, else: b2 - // b1(): - // store v1, Field 5 - // jmp b2() - // b2(): - // return - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id); - - let b1 = builder.insert_block(); - let b2 = builder.insert_block(); - - let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::Reference(Arc::new(Type::field()))); - - builder.terminate_with_jmpif(v0, b1, b2); - - builder.switch_to_block(b1); - let five = builder.field_constant(5u128); - builder.insert_store(v1, five); - builder.terminate_with_jmp(b2, vec![]); - - builder.switch_to_block(b2); - builder.terminate_with_return(vec![]); - - let ssa = builder.finish(); + let src = " + acir(inline) fn main f0 { + b0(v0: u1, v1: &mut Field): + jmpif v0 then: b1, else: b2 + b1(): + store Field 5 at v1 + jmp b2() + b2(): + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); - // Expected output: - // fn main f0 { - // b0(v0: u1, v1: reference): - // enable_side_effects v0 - // v4 = load v1 - // store Field 5 at v1 - // v5 = not v0 - // store v4 at v1 - // enable_side_effects u1 1 - // v6 = cast v0 as Field - // v7 = cast v5 as Field - // v8 = mul v6, Field 5 - // v9 = mul v7, v4 - // v10 = add v8, v9 - // store v10 at v1 - // return - // } + let expected = " + acir(inline) fn main f0 { + b0(v0: u1, v1: &mut Field): + enable_side_effects v0 + v2 = load v1 -> Field + store Field 5 at v1 + v4 = not v0 + store v2 at v1 + enable_side_effects u1 1 + v6 = cast v0 as Field + v7 = cast v4 as Field + v8 = mul v6, Field 5 + v9 = mul v7, v2 + v10 = add v8, v9 + store v10 at v1 + return + } + "; let ssa = ssa.flatten_cfg(); - let main = ssa.main(); - - assert_eq!(main.reachable_blocks().len(), 1); - - let store_count = count_instruction(main, |ins| matches!(ins, Instruction::Store { .. })); - assert_eq!(store_count, 3); + assert_ssa_equals(ssa, expected); } #[test] fn merge_stores_with_else_block() { - // fn main f0 { - // b0(v0: u1, v1: ref): - // jmpif v0, then: b1, else: b2 - // b1(): - // store Field 5 in v1 - // jmp b3() - // b2(): - // store Field 6 in v1 - // jmp b3() - // b3(): - // return - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id); - - let b1 = builder.insert_block(); - let b2 = builder.insert_block(); - let b3 = builder.insert_block(); - - let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::Reference(Arc::new(Type::field()))); - - builder.terminate_with_jmpif(v0, b1, b2); - - builder.switch_to_block(b1); - let five = builder.field_constant(5u128); - builder.insert_store(v1, five); - builder.terminate_with_jmp(b3, vec![]); - - builder.switch_to_block(b2); - let six = builder.field_constant(6u128); - builder.insert_store(v1, six); - builder.terminate_with_jmp(b3, vec![]); - - builder.switch_to_block(b3); - builder.terminate_with_return(vec![]); - - let ssa = builder.finish(); + let src = " + acir(inline) fn main f0 { + b0(v0: u1, v1: &mut Field): + jmpif v0 then: b1, else: b2 + b1(): + store Field 5 at v1 + jmp b3() + b2(): + store Field 6 at v1 + jmp b3() + b3(): + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); - // Expected output: - // fn main f0 { - // b0(v0: u1, v1: reference): - // enable_side_effects v0 - // v5 = load v1 - // store Field 5 at v1 - // v6 = not v0 - // store v5 at v1 - // enable_side_effects v6 - // v8 = load v1 - // store Field 6 at v1 - // enable_side_effects u1 1 - // v9 = cast v0 as Field - // v10 = cast v6 as Field - // v11 = mul v9, Field 5 - // v12 = mul v10, Field 6 - // v13 = add v11, v12 - // store v13 at v1 - // return - // } + let expected = " + acir(inline) fn main f0 { + b0(v0: u1, v1: &mut Field): + enable_side_effects v0 + v2 = load v1 -> Field + store Field 5 at v1 + v4 = not v0 + store v2 at v1 + enable_side_effects v4 + v5 = load v1 -> Field + store Field 6 at v1 + enable_side_effects u1 1 + v8 = cast v0 as Field + v9 = cast v4 as Field + v10 = mul v8, Field 5 + v11 = mul v9, Field 6 + v12 = add v10, v11 + store v12 at v1 + return + } + "; let ssa = ssa.flatten_cfg(); - let main = ssa.main(); - assert_eq!(main.reachable_blocks().len(), 1); - - let store_count = count_instruction(main, |ins| matches!(ins, Instruction::Store { .. })); - assert_eq!(store_count, 4); + assert_ssa_equals(ssa, expected); } fn count_instruction(function: &Function, f: impl Fn(&Instruction) -> bool) -> usize { From bacca20e19389ee656bc9f2fb37e9f3937972db8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 09:50:10 -0300 Subject: [PATCH 54/65] Allow comments in SSA --- .../noirc_evaluator/src/ssa/parser/lexer.rs | 8 ++++++ .../noirc_evaluator/src/ssa/parser/tests.rs | 26 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index e807c2c0a8d..cdaa80d072c 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -38,6 +38,14 @@ impl<'a> Lexer<'a> { } self.next_token() } + Some('/') if self.peek_char() == Some('/') => { + while let Some(char) = self.next_char() { + if char == '\n' { + break; + } + } + self.next_token() + } Some('=') if self.peek_char() == Some('=') => self.double_char_token(Token::Equal), Some('=') => self.single_char_token(Token::Assign), Some(',') => self.single_char_token(Token::Comma), diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 0c553e7c145..0a0765246a4 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -1,6 +1,9 @@ #![cfg(test)] -use crate::{ssa::Ssa, trim_leading_whitespace_from_lines}; +use crate::{ + ssa::{opt::assert_ssa_equals, Ssa}, + trim_leading_whitespace_from_lines, +}; fn assert_ssa_roundtrip(src: &str) { let ssa = Ssa::from_str(src).unwrap(); @@ -389,3 +392,24 @@ fn test_mutable_reference_type() { "; assert_ssa_roundtrip(src); } + +#[test] +fn test_parses_with_comments() { + let src = " + // This is a comment + acir(inline) fn main f0 { + b0(v0: &mut Field): // This is a block + return // Returns nothing + } + "; + + let expected = " + acir(inline) fn main f0 { + b0(v0: &mut Field): + return + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + assert_ssa_equals(ssa, expected); +} From 5da0cc03c45b7368075b49eabb324a11a5ccc86f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 09:55:46 -0300 Subject: [PATCH 55/65] Simplify a test that has a comment --- .../src/ssa/opt/flatten_cfg.rs | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 3d9a9af56e4..7857cda0e96 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -1364,40 +1364,29 @@ mod test { // Very simplified derived regression test for #1792 // Tests that it does not simplify to a true constraint an always-false constraint // The original function is replaced by the following: - // fn main f1 { - // b0(): - // jmpif u1 0 then: b1, else: b2 - // b1(): - // jmp b2() - // b2(): - // constrain u1 0 // was incorrectly removed - // return - // } - let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id); - - builder.insert_block(); // entry - - let b1 = builder.insert_block(); - let b2 = builder.insert_block(); - let v_true = builder.numeric_constant(true, Type::bool()); - let v_false = builder.numeric_constant(false, Type::bool()); - builder.terminate_with_jmpif(v_false, b1, b2); - - builder.switch_to_block(b1); - builder.terminate_with_jmp(b2, vec![]); - - builder.switch_to_block(b2); - builder.insert_constrain(v_false, v_true, None); // should not be removed - builder.terminate_with_return(vec![]); - - let ssa = builder.finish().flatten_cfg(); - let main = ssa.main(); + let src = " + acir(inline) fn main f1 { + b0(): + jmpif u1 0 then: b1, else: b2 + b1(): + jmp b2() + b2(): + constrain u1 0 == u1 1 // was incorrectly removed + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); - // Assert we have not incorrectly removed a constraint: - use Instruction::Constrain; - let constrain_count = count_instruction(main, |ins| matches!(ins, Constrain(..))); - assert_eq!(constrain_count, 1); + let expected = " + acir(inline) fn main f0 { + b0(): + enable_side_effects u1 1 + constrain u1 0 == u1 1 + return + } + "; + let ssa = ssa.flatten_cfg(); + assert_ssa_equals(ssa, expected); } #[test] From 8c53a41b17fb96d9789f7d6fe73c857090a3eee1 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 12:04:41 -0300 Subject: [PATCH 56/65] Refactor --- .../src/ssa/parser/into_ssa.rs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 15fed18f546..2a94a4fd1eb 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -13,14 +13,8 @@ use super::{ }; impl ParsedSsa { - pub(crate) fn into_ssa(mut self) -> Result { - let mut translator = Translator::new(&mut self)?; - - for function in self.functions { - translator.translate_function(function)?; - } - - Ok(translator.finish()) + pub(crate) fn into_ssa(self) -> Result { + Translator::translate(self) } } @@ -38,7 +32,21 @@ struct Translator { } impl Translator { + fn translate(mut parsed_ssa: ParsedSsa) -> Result { + let mut translator = Self::new(&mut parsed_ssa)?; + + // Note that the `new` call above removed the main function, + // so all we are left with are non-main functions. + for function in parsed_ssa.functions { + translator.translate_non_main_function(function)?; + } + + Ok(translator.finish()) + } + fn new(parsed_ssa: &mut ParsedSsa) -> Result { + // A FunctionBuilder must be created with a main Function, so here wer remove it + // from the parsed SSA to avoid adding it twice later on. let main_function = parsed_ssa.functions.remove(0); let main_id = FunctionId::new(0); let mut builder = FunctionBuilder::new(main_function.external_name.clone(), main_id); @@ -61,7 +69,7 @@ impl Translator { Ok(translator) } - fn translate_function(&mut self, function: ParsedFunction) -> Result<(), SsaError> { + fn translate_non_main_function(&mut self, function: ParsedFunction) -> Result<(), SsaError> { let function_id = self.functions[&function.internal_name]; let external_name = function.external_name.clone(); From 6cec6cbe873a6b24e9fd8373d9cd13ecbb9f95a7 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 13:08:49 -0300 Subject: [PATCH 57/65] Extract `parse_assignment` --- compiler/noirc_evaluator/src/ssa/parser.rs | 202 ++++++++++----------- 1 file changed, 97 insertions(+), 105 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index dd6500366e0..a485745340a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -197,111 +197,8 @@ impl<'a> Parser<'a> { } if let Some(target) = self.eat_identifier()? { - let mut targets = vec![target]; - - while self.eat(Token::Comma)? { - let target = self.eat_identifier_or_error()?; - targets.push(target); - } - - self.eat_or_error(Token::Assign)?; - - if self.eat_keyword(Keyword::Call)? { - let function = self.eat_identifier_or_error()?; - let arguments = self.parse_arguments()?; - self.eat_or_error(Token::Arrow)?; - let types = self.parse_types()?; - return Ok(Some(ParsedInstruction::Call { targets, function, arguments, types })); - } - - if targets.len() > 1 { - return Err(ParserError::MultipleReturnValuesOnlyAllowedForCall { - second_target: targets[1].clone(), - }); - } - - let target = targets.remove(0); - - if self.eat_keyword(Keyword::Allocate)? { - self.eat_or_error(Token::Arrow)?; - let typ = self.parse_type()?; - return Ok(Some(ParsedInstruction::Allocate { target, typ })); - } - - if self.eat_keyword(Keyword::ArrayGet)? { - let array = self.parse_value_or_error()?; - self.eat_or_error(Token::Comma)?; - self.eat_or_error(Token::Keyword(Keyword::Index))?; - let index = self.parse_value_or_error()?; - self.eat_or_error(Token::Arrow)?; - let element_type = self.parse_type()?; - return Ok(Some(ParsedInstruction::ArrayGet { - target, - element_type, - array, - index, - })); - } - - if self.eat_keyword(Keyword::ArraySet)? { - let mutable = self.eat_keyword(Keyword::Mut)?; - let array = self.parse_value_or_error()?; - self.eat_or_error(Token::Comma)?; - self.eat_or_error(Token::Keyword(Keyword::Index))?; - let index = self.parse_value_or_error()?; - self.eat_or_error(Token::Comma)?; - self.eat_or_error(Token::Keyword(Keyword::Value))?; - let value = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::ArraySet { - target, - array, - index, - value, - mutable, - })); - } - - if self.eat_keyword(Keyword::Cast)? { - let lhs = self.parse_value_or_error()?; - self.eat_or_error(Token::Keyword(Keyword::As))?; - let typ = self.parse_type()?; - return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); - } - - if self.eat_keyword(Keyword::Load)? { - let value = self.parse_value_or_error()?; - self.eat_or_error(Token::Arrow)?; - let typ = self.parse_type()?; - return Ok(Some(ParsedInstruction::Load { target, value, typ })); - } - - if self.eat_keyword(Keyword::Not)? { - let value = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::Not { target, value })); - } - - if self.eat_keyword(Keyword::Truncate)? { - let value = self.parse_value_or_error()?; - self.eat_or_error(Token::Keyword(Keyword::To))?; - let bit_size = self.eat_int_or_error()?.to_u128() as u32; - self.eat_or_error(Token::Keyword(Keyword::Bits))?; - self.eat_or_error(Token::Comma)?; - self.eat_or_error(Token::Keyword(Keyword::MaxBitSize))?; - self.eat_or_error(Token::Colon)?; - let max_bit_size = self.eat_int_or_error()?.to_u128() as u32; - return Ok(Some(ParsedInstruction::Truncate { - target, - value, - bit_size, - max_bit_size, - })); - } - - if let Some(op) = self.eat_binary_op()? { - let lhs = self.parse_value_or_error()?; - self.eat_or_error(Token::Comma)?; - let rhs = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::BinaryOp { target, lhs, op, rhs })); + if let Some(instruction) = self.parse_assignment(target)? { + return Ok(Some(instruction)); } return self.expected_instruction_or_terminator(); @@ -433,6 +330,101 @@ impl<'a> Parser<'a> { Ok(Some(ParsedInstruction::Store { address, value })) } + fn parse_assignment(&mut self, target: Identifier) -> ParseResult> { + let mut targets = vec![target]; + + while self.eat(Token::Comma)? { + let target = self.eat_identifier_or_error()?; + targets.push(target); + } + + self.eat_or_error(Token::Assign)?; + + if self.eat_keyword(Keyword::Call)? { + let function = self.eat_identifier_or_error()?; + let arguments = self.parse_arguments()?; + self.eat_or_error(Token::Arrow)?; + let types = self.parse_types()?; + return Ok(Some(ParsedInstruction::Call { targets, function, arguments, types })); + } + + if targets.len() > 1 { + return Err(ParserError::MultipleReturnValuesOnlyAllowedForCall { + second_target: targets[1].clone(), + }); + } + + let target = targets.remove(0); + + if self.eat_keyword(Keyword::Allocate)? { + self.eat_or_error(Token::Arrow)?; + let typ = self.parse_type()?; + return Ok(Some(ParsedInstruction::Allocate { target, typ })); + } + + if self.eat_keyword(Keyword::ArrayGet)? { + let array = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::Index))?; + let index = self.parse_value_or_error()?; + self.eat_or_error(Token::Arrow)?; + let element_type = self.parse_type()?; + return Ok(Some(ParsedInstruction::ArrayGet { target, element_type, array, index })); + } + + if self.eat_keyword(Keyword::ArraySet)? { + let mutable = self.eat_keyword(Keyword::Mut)?; + let array = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::Index))?; + let index = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::Value))?; + let value = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::ArraySet { target, array, index, value, mutable })); + } + + if self.eat_keyword(Keyword::Cast)? { + let lhs = self.parse_value_or_error()?; + self.eat_or_error(Token::Keyword(Keyword::As))?; + let typ = self.parse_type()?; + return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); + } + + if self.eat_keyword(Keyword::Load)? { + let value = self.parse_value_or_error()?; + self.eat_or_error(Token::Arrow)?; + let typ = self.parse_type()?; + return Ok(Some(ParsedInstruction::Load { target, value, typ })); + } + + if self.eat_keyword(Keyword::Not)? { + let value = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::Not { target, value })); + } + + if self.eat_keyword(Keyword::Truncate)? { + let value = self.parse_value_or_error()?; + self.eat_or_error(Token::Keyword(Keyword::To))?; + let bit_size = self.eat_int_or_error()?.to_u128() as u32; + self.eat_or_error(Token::Keyword(Keyword::Bits))?; + self.eat_or_error(Token::Comma)?; + self.eat_or_error(Token::Keyword(Keyword::MaxBitSize))?; + self.eat_or_error(Token::Colon)?; + let max_bit_size = self.eat_int_or_error()?.to_u128() as u32; + return Ok(Some(ParsedInstruction::Truncate { target, value, bit_size, max_bit_size })); + } + + if let Some(op) = self.eat_binary_op()? { + let lhs = self.parse_value_or_error()?; + self.eat_or_error(Token::Comma)?; + let rhs = self.parse_value_or_error()?; + return Ok(Some(ParsedInstruction::BinaryOp { target, lhs, op, rhs })); + } + + Ok(None) + } + fn parse_terminator(&mut self) -> ParseResult { if let Some(terminator) = self.parse_return()? { return Ok(terminator); From 8854b7db8d49381098382d3c31a703b3b8729e6a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 13:13:42 -0300 Subject: [PATCH 58/65] Simplify `eat_binary_op` --- compiler/noirc_evaluator/src/ssa/parser.rs | 64 ++++++---------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index a485745340a..2f94ea1fd0d 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -208,55 +208,25 @@ impl<'a> Parser<'a> { } fn eat_binary_op(&mut self) -> ParseResult> { - if self.eat_keyword(Keyword::Add)? { - return Ok(Some(BinaryOp::Add)); - } - - if self.eat_keyword(Keyword::Sub)? { - return Ok(Some(BinaryOp::Sub)); - } - - if self.eat_keyword(Keyword::Mul)? { - return Ok(Some(BinaryOp::Mul)); - } - - if self.eat_keyword(Keyword::Div)? { - return Ok(Some(BinaryOp::Div)); - } - - if self.eat_keyword(Keyword::Eq)? { - return Ok(Some(BinaryOp::Eq)); - } - - if self.eat_keyword(Keyword::Mod)? { - return Ok(Some(BinaryOp::Mod)); - } - - if self.eat_keyword(Keyword::Lt)? { - return Ok(Some(BinaryOp::Lt)); - } - - if self.eat_keyword(Keyword::And)? { - return Ok(Some(BinaryOp::And)); - } - - if self.eat_keyword(Keyword::Or)? { - return Ok(Some(BinaryOp::Or)); - } - - if self.eat_keyword(Keyword::Xor)? { - return Ok(Some(BinaryOp::Xor)); - } - - if self.eat_keyword(Keyword::Shl)? { - return Ok(Some(BinaryOp::Shl)); - } + let op = match self.token.token() { + Token::Keyword(Keyword::Add) => BinaryOp::Add, + Token::Keyword(Keyword::Sub) => BinaryOp::Sub, + Token::Keyword(Keyword::Mul) => BinaryOp::Mul, + Token::Keyword(Keyword::Div) => BinaryOp::Div, + Token::Keyword(Keyword::Eq) => BinaryOp::Eq, + Token::Keyword(Keyword::Mod) => BinaryOp::Mod, + Token::Keyword(Keyword::Lt) => BinaryOp::Lt, + Token::Keyword(Keyword::And) => BinaryOp::And, + Token::Keyword(Keyword::Or) => BinaryOp::Or, + Token::Keyword(Keyword::Xor) => BinaryOp::Xor, + Token::Keyword(Keyword::Shl) => BinaryOp::Shl, + Token::Keyword(Keyword::Shr) => BinaryOp::Shr, + _ => return Ok(None), + }; - if self.eat_keyword(Keyword::Shr)? { - return Ok(Some(BinaryOp::Shr)); - } + self.bump()?; - Ok(None) + Ok(Some(op)) } fn parse_call(&mut self) -> ParseResult> { From ed102605a4b63a4b0af27b10262dffe5e429ed9d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 11 Nov 2024 13:44:23 -0300 Subject: [PATCH 59/65] Error in parse_assignment, not outside --- compiler/noirc_evaluator/src/ssa/parser.rs | 28 ++++++++++------------ 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index 2f94ea1fd0d..c08849f311e 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -197,11 +197,7 @@ impl<'a> Parser<'a> { } if let Some(target) = self.eat_identifier()? { - if let Some(instruction) = self.parse_assignment(target)? { - return Ok(Some(instruction)); - } - - return self.expected_instruction_or_terminator(); + return Ok(Some(self.parse_assignment(target)?)); } Ok(None) @@ -300,7 +296,7 @@ impl<'a> Parser<'a> { Ok(Some(ParsedInstruction::Store { address, value })) } - fn parse_assignment(&mut self, target: Identifier) -> ParseResult> { + fn parse_assignment(&mut self, target: Identifier) -> ParseResult { let mut targets = vec![target]; while self.eat(Token::Comma)? { @@ -315,7 +311,7 @@ impl<'a> Parser<'a> { let arguments = self.parse_arguments()?; self.eat_or_error(Token::Arrow)?; let types = self.parse_types()?; - return Ok(Some(ParsedInstruction::Call { targets, function, arguments, types })); + return Ok(ParsedInstruction::Call { targets, function, arguments, types }); } if targets.len() > 1 { @@ -329,7 +325,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Allocate)? { self.eat_or_error(Token::Arrow)?; let typ = self.parse_type()?; - return Ok(Some(ParsedInstruction::Allocate { target, typ })); + return Ok(ParsedInstruction::Allocate { target, typ }); } if self.eat_keyword(Keyword::ArrayGet)? { @@ -339,7 +335,7 @@ impl<'a> Parser<'a> { let index = self.parse_value_or_error()?; self.eat_or_error(Token::Arrow)?; let element_type = self.parse_type()?; - return Ok(Some(ParsedInstruction::ArrayGet { target, element_type, array, index })); + return Ok(ParsedInstruction::ArrayGet { target, element_type, array, index }); } if self.eat_keyword(Keyword::ArraySet)? { @@ -351,26 +347,26 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::Comma)?; self.eat_or_error(Token::Keyword(Keyword::Value))?; let value = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::ArraySet { target, array, index, value, mutable })); + return Ok(ParsedInstruction::ArraySet { target, array, index, value, mutable }); } if self.eat_keyword(Keyword::Cast)? { let lhs = self.parse_value_or_error()?; self.eat_or_error(Token::Keyword(Keyword::As))?; let typ = self.parse_type()?; - return Ok(Some(ParsedInstruction::Cast { target, lhs, typ })); + return Ok(ParsedInstruction::Cast { target, lhs, typ }); } if self.eat_keyword(Keyword::Load)? { let value = self.parse_value_or_error()?; self.eat_or_error(Token::Arrow)?; let typ = self.parse_type()?; - return Ok(Some(ParsedInstruction::Load { target, value, typ })); + return Ok(ParsedInstruction::Load { target, value, typ }); } if self.eat_keyword(Keyword::Not)? { let value = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::Not { target, value })); + return Ok(ParsedInstruction::Not { target, value }); } if self.eat_keyword(Keyword::Truncate)? { @@ -382,17 +378,17 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::Keyword(Keyword::MaxBitSize))?; self.eat_or_error(Token::Colon)?; let max_bit_size = self.eat_int_or_error()?.to_u128() as u32; - return Ok(Some(ParsedInstruction::Truncate { target, value, bit_size, max_bit_size })); + return Ok(ParsedInstruction::Truncate { target, value, bit_size, max_bit_size }); } if let Some(op) = self.eat_binary_op()? { let lhs = self.parse_value_or_error()?; self.eat_or_error(Token::Comma)?; let rhs = self.parse_value_or_error()?; - return Ok(Some(ParsedInstruction::BinaryOp { target, lhs, op, rhs })); + return Ok(ParsedInstruction::BinaryOp { target, lhs, op, rhs }); } - Ok(None) + self.expected_instruction_or_terminator() } fn parse_terminator(&mut self) -> ParseResult { From 51af9146dfcfd68f76750cb76e2c56670ace450e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 12 Nov 2024 10:02:59 -0300 Subject: [PATCH 60/65] Make `allocate` return `-> &mut ...` --- .../noirc_evaluator/src/ssa/ir/printer.rs | 42 +++++++++---------- .../noirc_evaluator/src/ssa/opt/array_set.rs | 4 +- compiler/noirc_evaluator/src/ssa/opt/die.rs | 6 +-- compiler/noirc_evaluator/src/ssa/parser.rs | 26 ++++++++++-- .../noirc_evaluator/src/ssa/parser/tests.rs | 2 +- 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 9d765c6a1e4..21de85dbecb 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -8,8 +8,6 @@ use acvm::acir::circuit::{ErrorSelector, STRING_ERROR_SELECTOR}; use acvm::acir::AcirField; use iter_extended::vecmap; -use crate::ssa::ir::types::Type; - use super::{ basic_block::BasicBlockId, dfg::DataFlowGraph, @@ -184,26 +182,13 @@ fn display_instruction_inner( } Instruction::Call { func, arguments } => { let arguments = value_list(function, arguments); - let types = vecmap(results, |result| function.dfg.type_of_value(*result).to_string()); - if types.is_empty() { - writeln!(f, "call {}({})", show(*func), arguments) - } else if types.len() == 1 { - writeln!(f, "call {}({}) -> {}", show(*func), arguments, types[0]) - } else { - writeln!(f, "call {}({}) -> ({})", show(*func), arguments, types.join(", ")) - } + writeln!(f, "call {}({}){}", show(*func), arguments, result_types(function, results)) } Instruction::Allocate => { - assert_eq!(results.len(), 1); - let Type::Reference(typ) = function.dfg.type_of_value(results[0]) else { - panic!("Allocate instruction must have a reference type") - }; - writeln!(f, "allocate -> {}", typ) + writeln!(f, "allocate{}", result_types(function, results)) } Instruction::Load { address } => { - assert_eq!(results.len(), 1); - let typ = function.dfg.type_of_value(results[0]); - writeln!(f, "load {} -> {}", show(*address), typ) + writeln!(f, "load {}{}", show(*address), result_types(function, results)) } Instruction::Store { address, value } => { writeln!(f, "store {} at {}", show(*value), show(*address)) @@ -212,9 +197,13 @@ fn display_instruction_inner( writeln!(f, "enable_side_effects {}", show(*condition)) } Instruction::ArrayGet { array, index } => { - assert_eq!(results.len(), 1); - let typ = function.dfg.type_of_value(results[0]); - writeln!(f, "array_get {}, index {} -> {}", show(*array), show(*index), typ) + writeln!( + f, + "array_get {}, index {}{}", + show(*array), + show(*index), + result_types(function, results) + ) } Instruction::ArraySet { array, index, value, mutable } => { let array = show(*array); @@ -245,6 +234,17 @@ fn display_instruction_inner( } } +fn result_types(function: &Function, results: &[ValueId]) -> String { + let types = vecmap(results, |result| function.dfg.type_of_value(*result).to_string()); + if types.is_empty() { + String::new() + } else if types.len() == 1 { + format!(" -> {}", types[0]) + } else { + format!(" -> ({})", types.join(", ")) + } +} + /// Tries to extract a constant string from an error payload. pub(crate) fn try_to_extract_string_from_error_payload( error_selector: ErrorSelector, diff --git a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index 869c5fdb1a6..a4096a4e4b6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -216,9 +216,9 @@ mod tests { let src = " brillig(inline) fn main f0 { b0(): - v1 = allocate -> [Field; 5] + v1 = allocate -> &mut [Field; 5] store [[Field 0, Field 0, Field 0, Field 0, Field 0] of Field, [Field 0, Field 0, Field 0, Field 0, Field 0] of Field] of [Field; 5] at v1 - v6 = allocate -> [Field; 5] + v6 = allocate -> &mut [Field; 5] store [[Field 0, Field 0, Field 0, Field 0, Field 0] of Field, [Field 0, Field 0, Field 0, Field 0, Field 0] of Field] of [Field; 5] at v6 jmp b1(u32 0) b1(v0: u32): diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 37a93cf0f40..ffbe89a2f0d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -636,9 +636,9 @@ mod test { v5 = add v0, Field 2 jmp b1(v5) b1(v1: Field): - v6 = allocate -> Field + v6 = allocate -> &mut Field v7 = load v6 -> Field - v8 = allocate -> Field + v8 = allocate -> &mut Field store Field 1 at v8 v9 = load v8 -> Field v10 = add v9, Field 1 @@ -657,7 +657,7 @@ mod test { v3 = add v0, Field 2 jmp b1(v3) b1(v1: Field): - v4 = allocate -> Field + v4 = allocate -> &mut Field store Field 1 at v4 v6 = load v4 -> Field v7 = add v6, Field 1 diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index c08849f311e..cec05688f23 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -324,7 +324,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Allocate)? { self.eat_or_error(Token::Arrow)?; - let typ = self.parse_type()?; + let typ = self.parse_mutable_reference_type_or_error()?; return Ok(ParsedInstruction::Allocate { target, typ }); } @@ -588,15 +588,33 @@ impl<'a> Parser<'a> { return Ok(Type::Array(Arc::new(element_types), length.to_u128() as usize)); } - if self.eat(Token::Ampersand)? { - self.eat_or_error(Token::Keyword(Keyword::Mut))?; - let typ = self.parse_type()?; + if let Some(typ) = self.parse_mutable_reference_type()? { return Ok(Type::Reference(Arc::new(typ))); } self.expected_type() } + /// Parses `&mut Type`, returns `Type` if `&mut` was found, errors otherwise. + fn parse_mutable_reference_type_or_error(&mut self) -> ParseResult { + if let Some(typ) = self.parse_mutable_reference_type()? { + Ok(typ) + } else { + self.expected_token(Token::Ampersand) + } + } + + /// Parses `&mut Type`, returns `Some(Type)` if `&mut` was found, `None` otherwise. + fn parse_mutable_reference_type(&mut self) -> ParseResult> { + if !self.eat(Token::Ampersand)? { + return Ok(None); + } + + self.eat_or_error(Token::Keyword(Keyword::Mut))?; + let typ = self.parse_type()?; + Ok(Some(typ)) + } + fn eat_identifier_or_error(&mut self) -> ParseResult { if let Some(identifier) = self.eat_identifier()? { Ok(identifier) diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 0a0765246a4..b35d1da0a95 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -327,7 +327,7 @@ fn test_allocate() { let src = " acir(inline) fn main f0 { b0(): - v0 = allocate -> [Field; 3] + v0 = allocate -> &mut [Field; 3] return } "; From 280b5227cb014aa17f23cddfd45bd66f1e7ac82e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 12 Nov 2024 11:56:41 -0300 Subject: [PATCH 61/65] Show parse errors embedded in the original code --- compiler/noirc_evaluator/src/ssa/parser.rs | 106 ++++++++++++++++-- .../noirc_evaluator/src/ssa/parser/ast.rs | 8 ++ .../noirc_evaluator/src/ssa/parser/lexer.rs | 16 ++- .../noirc_evaluator/src/ssa/parser/token.rs | 93 ++++++++++++++- 4 files changed, 213 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/parser.rs b/compiler/noirc_evaluator/src/ssa/parser.rs index cec05688f23..bfdb08f85e6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser.rs +++ b/compiler/noirc_evaluator/src/ssa/parser.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::{ + fmt::{self, Debug, Formatter}, + sync::Arc, +}; use super::{ ir::{instruction::BinaryOp, types::Type}, @@ -13,6 +16,7 @@ use ast::{ use lexer::{Lexer, LexerError}; use noirc_errors::Span; use noirc_frontend::{monomorphization::ast::InlineType, token::IntType}; +use thiserror::Error; use token::{Keyword, SpannedToken, Token}; use crate::ssa::{ir::function::RuntimeType, parser::ast::ParsedTerminator}; @@ -24,23 +28,84 @@ mod tests; mod token; impl Ssa { - pub(crate) fn from_str(src: &str) -> Result { - let mut parser = Parser::new(src).map_err(SsaError::ParserError)?; - let parsed_ssa = parser.parse_ssa().map_err(SsaError::ParserError)?; - parsed_ssa.into_ssa() + pub(crate) fn from_str(src: &str) -> Result { + let mut parser = + Parser::new(src).map_err(|err| SsaErrorWithSource::parse_error(err, src))?; + let parsed_ssa = + parser.parse_ssa().map_err(|err| SsaErrorWithSource::parse_error(err, src))?; + parsed_ssa.into_ssa().map_err(|error| SsaErrorWithSource { src: src.to_string(), error }) } } -#[derive(Debug)] +pub(crate) struct SsaErrorWithSource { + src: String, + error: SsaError, +} + +impl SsaErrorWithSource { + fn parse_error(error: ParserError, src: &str) -> Self { + Self { src: src.to_string(), error: SsaError::ParserError(error) } + } +} + +impl Debug for SsaErrorWithSource { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let span = self.error.span(); + + let mut byte: usize = 0; + for line in self.src.lines() { + let has_error = + byte <= span.start() as usize && span.end() as usize <= byte + line.len(); + if has_error { + writeln!(f)?; + } + + writeln!(f, "{}", line)?; + + if has_error { + let offset = span.start() as usize - byte; + write!(f, "{}", " ".repeat(offset))?; + writeln!(f, "{}", "^".repeat((span.end() - span.start()) as usize))?; + write!(f, "{}", " ".repeat(offset))?; + writeln!(f, "{}", self.error)?; + writeln!(f)?; + } + + byte += line.len() + 1; // "+ 1" for the newline + } + Ok(()) + } +} + +#[derive(Debug, Error)] pub(crate) enum SsaError { + #[error("{0}")] ParserError(ParserError), + #[error("Unknown variable `{0}`")] UnknownVariable(Identifier), + #[error("Unknown block `{0}`")] UnknownBlock(Identifier), + #[error("Unknown function `{0}`")] UnknownFunction(Identifier), + #[error("Mismatched return values")] MismatchedReturnValues { returns: Vec, expected: usize }, + #[error("Variable `{0}` already defined")] VariableAlreadyDefined(Identifier), } +impl SsaError { + fn span(&self) -> Span { + match self { + SsaError::ParserError(parser_error) => parser_error.span(), + SsaError::UnknownVariable(identifier) + | SsaError::UnknownBlock(identifier) + | SsaError::VariableAlreadyDefined(identifier) + | SsaError::UnknownFunction(identifier) => identifier.span, + SsaError::MismatchedReturnValues { returns, expected: _ } => returns[0].span, + } + } +} + type ParseResult = Result; pub(crate) struct Parser<'a> { @@ -788,19 +853,46 @@ impl<'a> Parser<'a> { } } -#[derive(Debug)] +#[derive(Debug, Error)] pub(crate) enum ParserError { + #[error("{0}")] LexerError(LexerError), + #[error("Expected {token}, found {token}")] ExpectedToken { token: Token, found: Token, span: Span }, + #[error("Expected one of {tokens:?}, found {found}")] ExpectedOneOfTokens { tokens: Vec, found: Token, span: Span }, + #[error("Expected an identifier, found {found}")] ExpectedIdentifier { found: Token, span: Span }, + #[error("Expected an int, found {found}")] ExpectedInt { found: Token, span: Span }, + #[error("Expected a type, found {found}")] ExpectedType { found: Token, span: Span }, + #[error("Expected an instruction or terminator, found {found}")] ExpectedInstructionOrTerminator { found: Token, span: Span }, + #[error("Expected a value, found {found}")] ExpectedValue { found: Token, span: Span }, + #[error("Multiple return values only allowed for call")] MultipleReturnValuesOnlyAllowedForCall { second_target: Identifier }, } +impl ParserError { + fn span(&self) -> Span { + match self { + ParserError::LexerError(err) => err.span(), + ParserError::ExpectedToken { span, .. } + | ParserError::ExpectedOneOfTokens { span, .. } + | ParserError::ExpectedIdentifier { span, .. } + | ParserError::ExpectedInt { span, .. } + | ParserError::ExpectedType { span, .. } + | ParserError::ExpectedInstructionOrTerminator { span, .. } + | ParserError::ExpectedValue { span, .. } => *span, + ParserError::MultipleReturnValuesOnlyAllowedForCall { second_target, .. } => { + second_target.span + } + } + } +} + fn eof_spanned_token() -> SpannedToken { SpannedToken::new(Token::Eof, Default::default()) } diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 7fecf05e19f..f8fe8c68a98 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use acvm::FieldElement; use noirc_errors::Span; @@ -42,6 +44,12 @@ impl Identifier { } } +impl Display for Identifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name) + } +} + #[derive(Debug)] pub(crate) enum ParsedInstruction { Allocate { diff --git a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index cdaa80d072c..456e5ec15e0 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -5,6 +5,7 @@ use noirc_errors::{Position, Span}; use noirc_frontend::token::IntType; use num_bigint::BigInt; use num_traits::{Num, One}; +use thiserror::Error; use super::token::{Keyword, SpannedToken, Token}; @@ -237,9 +238,22 @@ impl<'a> Lexer<'a> { type SpannedTokenResult = Result; -#[derive(Debug)] +#[derive(Debug, Error)] pub(crate) enum LexerError { + #[error("Unexpected character: {char}")] UnexpectedCharacter { char: char, span: Span }, + #[error("Invalid integer literal")] InvalidIntegerLiteral { span: Span, found: String }, + #[error("Integer literal too large")] IntegerLiteralTooLarge { span: Span, limit: String }, } + +impl LexerError { + pub(crate) fn span(&self) -> Span { + match self { + LexerError::UnexpectedCharacter { span, .. } + | LexerError::InvalidIntegerLiteral { span, .. } + | LexerError::IntegerLiteralTooLarge { span, .. } => *span, + } + } +} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index b9cf94139da..41c4f9ca164 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use acvm::FieldElement; use noirc_errors::{Position, Span, Spanned}; use noirc_frontend::token::IntType; @@ -23,7 +25,7 @@ impl SpannedToken { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Token { Ident(String), Int(FieldElement), @@ -68,7 +70,38 @@ impl Token { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +impl Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Token::Ident(ident) => write!(f, "{}", ident), + Token::Int(int) => write!(f, "{}", int), + Token::Keyword(keyword) => write!(f, "{}", keyword), + Token::IntType(int_type) => write!(f, "{}", int_type), + Token::Assign => write!(f, "="), + Token::LeftParen => write!(f, "("), + Token::RightParen => write!(f, ")"), + Token::LeftBrace => write!(f, "{{"), + Token::RightBrace => write!(f, "}}"), + Token::LeftBracket => write!(f, "["), + Token::RightBracket => write!(f, "]"), + Token::Comma => write!(f, ","), + Token::Colon => write!(f, ":"), + Token::Semicolon => write!(f, ";"), + Token::Arrow => write!(f, "->"), + Token::Equal => write!(f, "=="), + Token::Ampersand => write!(f, "&"), + Token::Eof => write!(f, "(end of stream)"), + } + } +} + +impl std::fmt::Debug for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Keyword { Acir, Add, @@ -178,3 +211,59 @@ impl Keyword { Some(Token::Keyword(keyword)) } } + +impl Display for Keyword { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Keyword::Acir => write!(f, "acir"), + Keyword::Add => write!(f, "add"), + Keyword::Allocate => write!(f, "allocate"), + Keyword::And => write!(f, "and"), + Keyword::ArrayGet => write!(f, "array_get"), + Keyword::ArraySet => write!(f, "array_set"), + Keyword::As => write!(f, "as"), + Keyword::At => write!(f, "at"), + Keyword::Bits => write!(f, "bits"), + Keyword::Bool => write!(f, "bool"), + Keyword::Brillig => write!(f, "brillig"), + Keyword::Call => write!(f, "call"), + Keyword::Cast => write!(f, "cast"), + Keyword::Constrain => write!(f, "constrain"), + Keyword::DecRc => write!(f, "dec_rc"), + Keyword::Div => write!(f, "div"), + Keyword::Else => write!(f, "else"), + Keyword::EnableSideEffects => write!(f, "enable_side_effects"), + Keyword::Eq => write!(f, "eq"), + Keyword::Field => write!(f, "Field"), + Keyword::Fold => write!(f, "fold"), + Keyword::Fn => write!(f, "fn"), + Keyword::IncRc => write!(f, "inc_rc"), + Keyword::Index => write!(f, "index"), + Keyword::Inline => write!(f, "inline"), + Keyword::InlineAlways => write!(f, "inline_always"), + Keyword::Jmp => write!(f, "jmp"), + Keyword::Jmpif => write!(f, "jmpif"), + Keyword::Load => write!(f, "load"), + Keyword::Lt => write!(f, "lt"), + Keyword::MaxBitSize => write!(f, "max_bit_size"), + Keyword::Mod => write!(f, "mod"), + Keyword::Mul => write!(f, "mul"), + Keyword::Mut => write!(f, "mut"), + Keyword::NoPredicates => write!(f, "no_predicates"), + Keyword::Not => write!(f, "not"), + Keyword::Of => write!(f, "of"), + Keyword::Or => write!(f, "or"), + Keyword::RangeCheck => write!(f, "range_check"), + Keyword::Return => write!(f, "return"), + Keyword::Shl => write!(f, "shl"), + Keyword::Shr => write!(f, "shr"), + Keyword::Store => write!(f, "store"), + Keyword::Sub => write!(f, "sub"), + Keyword::Then => write!(f, "then"), + Keyword::To => write!(f, "to"), + Keyword::Truncate => write!(f, "truncate"), + Keyword::Value => write!(f, "value"), + Keyword::Xor => write!(f, "xor"), + } + } +} From 8825b251bfe2ccb274f99d592f0c918539c0bde8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 12 Nov 2024 12:01:20 -0300 Subject: [PATCH 62/65] Swap arguments order in assert_ssa_equals --- compiler/noirc_evaluator/src/ssa/opt/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index c82687374e4..a96e3cacc08 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -34,7 +34,7 @@ pub(crate) fn assert_ssa_equals(mut ssa: super::Ssa, expected: &str) { let expected = trim_leading_whitespace_from_lines(expected); if ssa != expected { - println!("Expected:\n~~~\n{}\n~~~\nGot:\n~~~\n{}\n~~~", expected, ssa); - similar_asserts::assert_eq!(expected, ssa); + println!("Got:\n~~~\n{}\n~~~\nExpected:\n~~~\n{}\n~~~", ssa, expected); + similar_asserts::assert_eq!(ssa, expected); } } From 757c8bd02a5b8ef43ad5830a98ab454361899d17 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 12 Nov 2024 12:06:20 -0300 Subject: [PATCH 63/65] Error early if expected SSA is not valid SSA --- compiler/noirc_evaluator/src/ssa/opt/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index a96e3cacc08..372b3b67648 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -25,7 +25,14 @@ mod unrolling; #[cfg(test)] pub(crate) fn assert_ssa_equals(mut ssa: super::Ssa, expected: &str) { - use crate::trim_leading_whitespace_from_lines; + // First check if `expected` is valid SSA by parsing it, otherwise + // the comparison will always fail but it won't be clear that it's because + // expected is not valid. + if let Err(err) = Ssa::from_str(expected) { + panic!("`expected` argument of `assert_ssa_equals` is not valid SSA:\n{:?}", err); + } + + use crate::{ssa::Ssa, trim_leading_whitespace_from_lines}; ssa.normalize_ids(); From cdfb50514e9d26cd76230e10f423ada0ceb1df32 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 12 Nov 2024 12:11:44 -0300 Subject: [PATCH 64/65] assert_ssa_equals -> assert_normalized_ssa_equals --- .../noirc_evaluator/src/ssa/opt/array_set.rs | 4 ++-- .../src/ssa/opt/constant_folding.rs | 20 +++++++++---------- compiler/noirc_evaluator/src/ssa/opt/die.rs | 12 +++++------ .../src/ssa/opt/flatten_cfg.rs | 12 +++++------ compiler/noirc_evaluator/src/ssa/opt/mod.rs | 5 ++++- .../noirc_evaluator/src/ssa/parser/tests.rs | 4 ++-- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index a4096a4e4b6..e7f69f87da8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -206,7 +206,7 @@ fn make_mutable( #[cfg(test)] mod tests { - use crate::ssa::{opt::assert_ssa_equals, Ssa}; + use crate::ssa::{opt::assert_normalized_ssa_equals, Ssa}; #[test] fn array_set_in_loop_with_conditional_clone() { @@ -247,6 +247,6 @@ mod tests { // We expect the same result as above let ssa = ssa.array_set_optimization(); - assert_ssa_equals(ssa, src); + assert_normalized_ssa_equals(ssa, src); } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index b9b4f6fdcc2..728a8f61d39 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -362,7 +362,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{map::Id, types::Type}, - opt::assert_ssa_equals, + opt::assert_normalized_ssa_equals, Ssa, }; @@ -396,7 +396,7 @@ mod test { } "; let ssa = ssa.fold_constants(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -434,7 +434,7 @@ mod test { "; let ssa = ssa.fold_constants(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -473,7 +473,7 @@ mod test { "; let ssa = ssa.fold_constants(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -489,7 +489,7 @@ mod test { "; let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); - assert_ssa_equals(ssa, src); + assert_normalized_ssa_equals(ssa, src); } #[test] @@ -517,7 +517,7 @@ mod test { "; let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -552,7 +552,7 @@ mod test { let ssa = Ssa::from_str(src).unwrap(); let ssa = ssa.fold_constants(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -584,7 +584,7 @@ mod test { return } "; - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } // Regression for #4600 @@ -609,7 +609,7 @@ mod test { // Expected output is unchanged let ssa = ssa.fold_constants(); - assert_ssa_equals(ssa, src); + assert_normalized_ssa_equals(ssa, src); } #[test] @@ -659,7 +659,7 @@ mod test { "; let ssa = ssa.fold_constants_using_constraints(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } // This test currently fails. It being fixed will address the issue https://github.com/noir-lang/noir/issues/5756 diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index ffbe89a2f0d..57af27e8dcd 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -623,7 +623,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{instruction::Instruction, map::Id, types::Type}, - opt::assert_ssa_equals, + opt::assert_normalized_ssa_equals, Ssa, }; @@ -667,7 +667,7 @@ mod test { } "; let ssa = ssa.dead_instruction_elimination(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -691,7 +691,7 @@ mod test { } "; let ssa = ssa.dead_instruction_elimination(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -715,7 +715,7 @@ mod test { } "; let ssa = ssa.dead_instruction_elimination(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -733,7 +733,7 @@ mod test { // We expect the output to be unchanged let ssa = ssa.dead_instruction_elimination(); - assert_ssa_equals(ssa, src); + assert_normalized_ssa_equals(ssa, src); } #[test] @@ -873,6 +873,6 @@ mod test { } "; let ssa = ssa.dead_instruction_elimination(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 7857cda0e96..db2d96aac81 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -930,7 +930,7 @@ mod test { types::Type, value::{Value, ValueId}, }, - opt::assert_ssa_equals, + opt::assert_normalized_ssa_equals, Ssa, }; @@ -967,7 +967,7 @@ mod test { "; let ssa = ssa.flatten_cfg(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -999,7 +999,7 @@ mod test { "; let ssa = ssa.flatten_cfg(); assert_eq!(ssa.main().reachable_blocks().len(), 1); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -1036,7 +1036,7 @@ mod test { } "; let ssa = ssa.flatten_cfg(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] @@ -1079,7 +1079,7 @@ mod test { } "; let ssa = ssa.flatten_cfg(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } fn count_instruction(function: &Function, f: impl Fn(&Instruction) -> bool) -> usize { @@ -1386,7 +1386,7 @@ mod test { } "; let ssa = ssa.flatten_cfg(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } #[test] diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 372b3b67648..5576b494570 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -23,8 +23,11 @@ mod runtime_separation; mod simplify_cfg; mod unrolling; +/// Asserts that the given SSA, after normalizing its IDs and printing it, +/// is equal to the expected strings. Normalization is done so the IDs don't +/// shift depending on whether temporary intermediate values were created. #[cfg(test)] -pub(crate) fn assert_ssa_equals(mut ssa: super::Ssa, expected: &str) { +pub(crate) fn assert_normalized_ssa_equals(mut ssa: super::Ssa, expected: &str) { // First check if `expected` is valid SSA by parsing it, otherwise // the comparison will always fail but it won't be clear that it's because // expected is not valid. diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index b35d1da0a95..1ec0161649b 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -1,7 +1,7 @@ #![cfg(test)] use crate::{ - ssa::{opt::assert_ssa_equals, Ssa}, + ssa::{opt::assert_normalized_ssa_equals, Ssa}, trim_leading_whitespace_from_lines, }; @@ -411,5 +411,5 @@ fn test_parses_with_comments() { "; let ssa = Ssa::from_str(src).unwrap(); - assert_ssa_equals(ssa, expected); + assert_normalized_ssa_equals(ssa, expected); } From 9e6dc2da5d3ffdb8037acaec2ffb84760d8b5aa5 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 12 Nov 2024 12:22:43 -0300 Subject: [PATCH 65/65] Fix test --- compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 728a8f61d39..de8e5b25926 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -421,14 +421,14 @@ mod test { // Note that this constant guarantees that `v0/constant < 2^8`. We then do not need to truncate the result. let constant = 2_u128.pow(8); - let constant = main.dfg.make_constant(constant.into(), Type::field()); + let constant = main.dfg.make_constant(constant.into(), Type::unsigned(16)); main.dfg.set_value_from_id(v1, constant); let expected = " acir(inline) fn main f0 { - b0(v0: u16, v1: Field): - v3 = div v0, Field 256 + b0(v0: u16, v1: u16): + v3 = div v0, u16 256 return v3 } ";