diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 5475898847b..cbeaa4ea194 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -30,6 +30,7 @@ enum ArrayLiteralOrError { } impl Parser<'_> { + #[inline(always)] pub(crate) fn parse_expression_or_error(&mut self) -> Expression { self.parse_expression_or_error_impl(true) // allow constructors } @@ -48,6 +49,7 @@ impl Parser<'_> { self.parse_expression_or_error_impl(false) // allow constructors } + #[inline(always)] pub(crate) fn parse_expression_or_error_impl( &mut self, allow_constructors: bool, diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index f4db01fd9de..10a94932782 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -8,180 +8,201 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +macro_rules! parse_infix { + ($self:expr, $next:expr, $operator:expr, $allow_constructors:expr) => {{ + let start_location = $self.current_token_location; + let mut lhs = $next($self, $allow_constructors)?; + + loop { + let operator_start_location = $self.current_token_location; + let operator = $operator; + let operator = Located::from(operator_start_location, operator); + + let Some(rhs) = $next($self, $allow_constructors) else { + $self.push_expected_expression(); + break; + }; + + lhs = $self.new_infix_expression(lhs, operator, rhs, start_location); + } + + Some(lhs) + }}; +} + +impl Parser<'_> { /// EqualOrNotEqualExpression /// = OrExpression ( ( '==' | '!=' ) OrExpression )* + #[inline(always)] pub(super) fn parse_equal_or_not_equal( &mut self, allow_constructors: bool, ) -> Option { - self.parse_infix(allow_constructors, Parser::parse_or, |parser| { - if parser.eat(Token::Equal) { - Some(BinaryOpKind::Equal) - } else if parser.eat(Token::NotEqual) { - Some(BinaryOpKind::NotEqual) + parse_infix!( + self, + Parser::parse_or, + if self.eat(Token::Equal) { + BinaryOpKind::Equal + } else if self.eat(Token::NotEqual) { + BinaryOpKind::NotEqual } else { - None - } - }) + break; + }, + allow_constructors + ) } /// OrExpression /// = AndExpression ( '|' AndExpression )* + #[inline(always)] pub(super) fn parse_or(&mut self, allow_constructors: bool) -> Option { - self.parse_infix(allow_constructors, Parser::parse_and, |parser| { - // Don't parse `x |= ...`, etc. - if parser.next_is(Token::Assign) { - None - } else if parser.eat(Token::Pipe) { - Some(BinaryOpKind::Or) + parse_infix!( + self, + Parser::parse_and, + if self.next_is(Token::Assign) { + break; + } else if self.eat(Token::Pipe) { + BinaryOpKind::Or } else { - None - } - }) + break; + }, + allow_constructors + ) } /// AndExpression /// = XorExpression ( '&' XorExpression )* + #[inline(always)] pub(super) fn parse_and(&mut self, allow_constructors: bool) -> Option { - self.parse_infix(allow_constructors, Parser::parse_xor, |parser| { + parse_infix!( + self, + Parser::parse_xor, // Don't parse `x |= ...`, etc. - if parser.next_is(Token::Assign) { - None - } else if parser.eat(Token::Ampersand) { - Some(BinaryOpKind::And) - } else if parser.eat(Token::LogicalAnd) { - parser.push_error(ParserErrorReason::LogicalAnd, parser.previous_token_location); - Some(BinaryOpKind::And) + if self.next_is(Token::Assign) { + break; + } else if self.eat(Token::Ampersand) { + BinaryOpKind::And + } else if self.eat(Token::LogicalAnd) { + self.push_error(ParserErrorReason::LogicalAnd, self.previous_token_location); + BinaryOpKind::And } else { - None - } - }) + break; + }, + allow_constructors + ) } /// XorExpression /// = LessOrGreaterExpression ( '^' LessOrGreaterExpression )* + #[inline(always)] pub(super) fn parse_xor(&mut self, allow_constructors: bool) -> Option { - self.parse_infix(allow_constructors, Parser::parse_less_or_greater, |parser| { + parse_infix!( + self, + Parser::parse_less_or_greater, // Don't parse `x |= ...`, etc. - if parser.next_is(Token::Assign) { - None - } else if parser.eat(Token::Caret) { - Some(BinaryOpKind::Xor) + if self.next_is(Token::Assign) { + break; + } else if self.eat(Token::Caret) { + BinaryOpKind::Xor } else { - None - } - }) + break; + }, + allow_constructors + ) } /// LessOrGreaterExpression /// = ShiftExpression ( ( '<' | '<=' | '>' | '>=' ) ShiftExpression )* + #[inline(always)] pub(super) fn parse_less_or_greater(&mut self, allow_constructors: bool) -> Option { - self.parse_infix(allow_constructors, Parser::parse_shift, |parser| { - if parser.eat(Token::Less) { - Some(BinaryOpKind::Less) - } else if parser.eat(Token::LessEqual) { - Some(BinaryOpKind::LessEqual) - } else if parser.next_token.token() != &Token::GreaterEqual - && parser.eat(Token::Greater) - { + parse_infix!( + self, + Parser::parse_shift, + if self.eat(Token::Less) { + BinaryOpKind::Less + } else if self.eat(Token::LessEqual) { + BinaryOpKind::LessEqual + } else if self.next_token.token() != &Token::GreaterEqual && self.eat(Token::Greater) { // Make sure to skip the `>>=` case, as `>>=` is lexed as `> >=`. - Some(BinaryOpKind::Greater) - } else if parser.eat(Token::GreaterEqual) { - Some(BinaryOpKind::GreaterEqual) + BinaryOpKind::Greater + } else if self.eat(Token::GreaterEqual) { + BinaryOpKind::GreaterEqual } else { - None - } - }) + break; + }, + allow_constructors + ) } /// ShiftExpression /// = AddOrSubtractExpression ( ( '<<' | '>' '>' ) AddOrSubtractExpression )* + #[inline(always)] pub(super) fn parse_shift(&mut self, allow_constructors: bool) -> Option { - self.parse_infix(allow_constructors, Parser::parse_add_or_subtract, |parser| { - if !parser.next_is(Token::Assign) && parser.eat(Token::ShiftLeft) { - Some(BinaryOpKind::ShiftLeft) - } else if parser.at(Token::Greater) && parser.next_is(Token::Greater) { + parse_infix!( + self, + Parser::parse_add_or_subtract, + if !self.next_is(Token::Assign) && self.eat(Token::ShiftLeft) { + BinaryOpKind::ShiftLeft + } else if self.at(Token::Greater) && self.next_is(Token::Greater) { // Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier // to parse nested generic types. For normal expressions however, it means we have to manually // parse two greater-than tokens as a single right-shift here. - parser.bump(); - parser.bump(); - Some(BinaryOpKind::ShiftRight) + self.bump(); + self.bump(); + BinaryOpKind::ShiftRight } else { - None - } - }) + break; + }, + allow_constructors + ) } /// AddOrSubtractExpression /// = MultiplyOrDivideOrModuloExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloExpression )* + #[inline(always)] pub(super) fn parse_add_or_subtract(&mut self, allow_constructors: bool) -> Option { - self.parse_infix(allow_constructors, Parser::parse_multiply_or_divide_or_modulo, |parser| { - if parser.next_is(Token::Assign) { - None - } else if parser.eat(Token::Plus) { - Some(BinaryOpKind::Add) - } else if parser.eat(Token::Minus) { - Some(BinaryOpKind::Subtract) + parse_infix!( + self, + Parser::parse_multiply_or_divide_or_modulo, + if self.next_is(Token::Assign) { + break; + } else if self.eat(Token::Plus) { + BinaryOpKind::Add + } else if self.eat(Token::Minus) { + BinaryOpKind::Subtract } else { - None - } - }) + break; + }, + allow_constructors + ) } /// MultiplyOrDivideOrModuloExpression /// = Term ( ( '*' | '/' | '%' ) Term )* + #[inline(always)] pub(super) fn parse_multiply_or_divide_or_modulo( &mut self, allow_constructors: bool, ) -> Option { - self.parse_infix(allow_constructors, Parser::parse_term, |parser| { - if parser.next_is(Token::Assign) { - None - } else if parser.eat(Token::Star) { - Some(BinaryOpKind::Multiply) - } else if parser.eat(Token::Slash) { - Some(BinaryOpKind::Divide) - } else if parser.eat(Token::Percent) { - Some(BinaryOpKind::Modulo) - } else { - None - } - }) - } - - fn parse_infix( - &mut self, - allow_constructors: bool, - mut next: Next, - mut op: Op, - ) -> Option - where - Next: FnMut(&mut Parser<'a>, bool) -> Option, - Op: FnMut(&mut Parser<'a>) -> Option, - { - let start_location = self.current_token_location; - let mut lhs = next(self, allow_constructors)?; - - loop { - let operator_start_location = self.current_token_location; - let Some(operator) = op(self) else { + parse_infix!( + self, + Parser::parse_term, + if self.next_is(Token::Assign) { break; - }; - let operator = Located::from(operator_start_location, operator); - - let Some(rhs) = next(self, allow_constructors) else { - self.push_expected_expression(); + } else if self.eat(Token::Star) { + BinaryOpKind::Multiply + } else if self.eat(Token::Slash) { + BinaryOpKind::Divide + } else if self.eat(Token::Percent) { + BinaryOpKind::Modulo + } else { break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_location); - } - - Some(lhs) + }, + allow_constructors + ) } + #[inline(always)] fn new_infix_expression( &self, lhs: Expression, diff --git a/tooling/nargo/src/lib.rs b/tooling/nargo/src/lib.rs index e54f83fd45f..d79e7f24556 100644 --- a/tooling/nargo/src/lib.rs +++ b/tooling/nargo/src/lib.rs @@ -17,6 +17,9 @@ pub mod workspace; pub use self::errors::NargoError; pub use self::ops::FuzzExecutionConfig; pub use self::ops::FuzzFolderConfig; +use std::sync::Mutex; +use std::sync::mpsc; +use std::thread; use std::{ collections::{BTreeMap, HashMap, HashSet}, path::PathBuf, @@ -29,7 +32,6 @@ use noirc_frontend::{ hir::{Context, ParsedFiles, def_map::parse_file}, }; use package::{Dependency, Package}; -use rayon::prelude::*; use walkdir::WalkDir; pub fn prepare_dependencies( @@ -165,7 +167,13 @@ pub fn insert_all_files_under_path_into_file_manager( } } +const STACK_SIZE: usize = 8 * 1024 * 1024; + +#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] pub fn parse_all(file_manager: &FileManager) -> ParsedFiles { + use rayon::iter::ParallelBridge as _; + use rayon::iter::ParallelIterator as _; + file_manager .as_file_map() .all_file_ids() @@ -180,6 +188,58 @@ pub fn parse_all(file_manager: &FileManager) -> ParsedFiles { .collect() } +#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +pub fn parse_all(file_manager: &FileManager) -> ParsedFiles { + let num_threads = rayon::current_num_threads(); + let (sender, receiver) = mpsc::channel(); + let iter = &Mutex::new(file_manager.as_file_map().all_file_ids()); + + thread::scope(|scope| { + // Start worker threads + for _ in 0..num_threads { + // Clone sender so it's dropped once the thread finishes + let thread_sender = sender.clone(); + thread::Builder::new() + // Specify a larger-than-default stack size to prevent overflowing stack in large programs. + // (the default is 2MB) + .stack_size(STACK_SIZE) + .spawn_scoped(scope, move || { + loop { + // Get next file to process from the iterator. + let Some(&file_id) = iter.lock().unwrap().next() else { + break; + }; + + let file_path = file_manager.path(file_id).expect("expected file to exist"); + let file_extension = file_path + .extension() + .expect("expected all file paths to have an extension"); + if file_extension != "nr" { + continue; + } + + let parsed_file = parse_file(file_manager, file_id); + + if thread_sender.send((file_id, parsed_file)).is_err() { + break; + } + } + }) + .unwrap(); + } + + // Also drop main sender so the channel closes + drop(sender); + + let mut parsed_files = ParsedFiles::default(); + while let Ok((file_id, parsed_file)) = receiver.recv() { + parsed_files.insert(file_id, parsed_file); + } + + parsed_files + }) +} + #[tracing::instrument(level = "trace", skip_all)] pub fn prepare_package<'file_manager, 'parsed_files>( file_manager: &'file_manager FileManager,