From 2942c9aa7e58b4d4d0d5b2a7e0430aeeecf79509 Mon Sep 17 00:00:00 2001 From: evan-schott <53463459+evan-schott@users.noreply.github.com> Date: Tue, 3 Oct 2023 16:11:17 -0700 Subject: [PATCH] constant time lookups --- compiler/parser/src/parser/file.rs | 2 +- .../src/code_generation/visit_program.rs | 12 ++++++++-- .../src/function_inlining/function_inliner.rs | 2 ++ .../src/function_inlining/inline_program.rs | 23 ++++++++++++------- .../define_multiple_variables_fail.out | 2 +- .../compiler/definition/tuple_def_fail.out | 5 ++++ .../compiler/definition/tuple_def_fail.leo | 2 +- 7 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 tests/expectations/compiler/definition/tuple_def_fail.out diff --git a/compiler/parser/src/parser/file.rs b/compiler/parser/src/parser/file.rs index aab3425dc0..a7205f3c87 100644 --- a/compiler/parser/src/parser/file.rs +++ b/compiler/parser/src/parser/file.rs @@ -147,7 +147,7 @@ impl ParserContext<'_> { match &self.token.token { Token::Const => { let declaration = self.parse_const_declaration_statement()?; - consts.push((Symbol::intern(&declaration.place.to_string()), definition)); + consts.push((Symbol::intern(&declaration.place.to_string()), declaration)); } Token::Struct | Token::Record => { let (id, struct_) = self.parse_struct()?; diff --git a/compiler/passes/src/code_generation/visit_program.rs b/compiler/passes/src/code_generation/visit_program.rs index 9a17f744e8..208cfba365 100644 --- a/compiler/passes/src/code_generation/visit_program.rs +++ b/compiler/passes/src/code_generation/visit_program.rs @@ -57,14 +57,22 @@ impl<'a> CodeGenerator<'a> { // Note that the unwrap is safe since type checking guarantees that the struct dependency graph is acyclic. let order = self.struct_graph.post_order().unwrap(); + // Create a mapping of symbols to references of structs so can perform constant-time lookups. + let structs_map: IndexMap = program_scope.structs.iter().map(|(name, struct_)| { + ( + *name, + struct_ + ) + }).collect(); + // Visit each `Struct` or `Record` in the post-ordering and produce an Aleo struct or record. program_string.push_str( &order .into_iter() .map(|name| { - match program_scope.structs.iter().find(|(identifier, _)| *identifier == name) { + match structs_map.get(&name) { // If the struct is found, it is a local struct. - Some((_, struct_)) => self.visit_struct_or_record(struct_), + Some(struct_) => self.visit_struct_or_record(*struct_), // If the struct is not found, it is an imported struct. None => String::new(), } diff --git a/compiler/passes/src/function_inlining/function_inliner.rs b/compiler/passes/src/function_inlining/function_inliner.rs index e1e492dd4b..d308970a12 100644 --- a/compiler/passes/src/function_inlining/function_inliner.rs +++ b/compiler/passes/src/function_inlining/function_inliner.rs @@ -19,6 +19,8 @@ use crate::{Assigner, AssignmentRenamer, CallGraph}; use leo_ast::{Function, NodeBuilder}; use leo_span::Symbol; +use indexmap::IndexMap; + pub struct FunctionInliner<'a> { /// A counter used to create unique NodeIDs. pub(crate) node_builder: &'a NodeBuilder, diff --git a/compiler/passes/src/function_inlining/inline_program.rs b/compiler/passes/src/function_inlining/inline_program.rs index ccbf0e65b2..95068ffde6 100644 --- a/compiler/passes/src/function_inlining/inline_program.rs +++ b/compiler/passes/src/function_inlining/inline_program.rs @@ -15,8 +15,10 @@ // along with the Leo library. If not, see . use crate::FunctionInliner; +use indexmap::{IndexMap, IndexSet}; -use leo_ast::{ProgramReconstructor, ProgramScope}; +use leo_ast::{Function, ProgramReconstructor, ProgramScope}; +use leo_span::Symbol; impl ProgramReconstructor for FunctionInliner<'_> { fn reconstruct_program_scope(&mut self, mut input: ProgramScope) -> ProgramScope { @@ -25,25 +27,30 @@ impl ProgramReconstructor for FunctionInliner<'_> { // Note that the unwrap is safe since type checking guarantees that the call graph is acyclic. let order = self.call_graph.post_order().unwrap(); + // Construct map to provide faster lookup of functions + let function_map: IndexMap = input.functions.clone().into_iter().collect(); + + // Construct set to keep track of the remaining functions that still need to be processed. + let mut function_set: IndexSet = function_map.keys().cloned().collect(); + // Reconstruct and accumulate each of the functions in post-order. for function_name in order.into_iter() { // None: If `function_name` is not in `input.functions`, then it must be an external function. // TODO: Check that this is indeed an external function. Requires a redesign of the symbol table. - - if let Some(pos) = input.functions.iter().position(|(symbol, _)| *symbol == function_name) { - let (_, function) = input.functions.remove(pos); + if let Some(function) = function_map.get(&function_name) { + function_set.remove(&function_name); // Reconstruct the function. - let reconstructed_function = self.reconstruct_function(function); + let reconstructed_function = self.reconstruct_function(function.clone()); // Add the reconstructed function to the mapping. self.reconstructed_functions.insert(function_name, reconstructed_function); } } - // Check that `input.functions` is empty. // This is a sanity check to ensure that functions in the program scope have been processed. - assert!(input.functions.is_empty(), "All functions in the program scope should have been processed."); + assert!(function_set.is_empty(), "All functions in the program scope should have been processed."); + input.functions.clear(); // Note that this intentionally clears `self.reconstructed_functions` for the next program scope. - let functions = core::mem::take(&mut self.reconstructed_functions).into_iter().map(|(symbol, function)| (*symbol, function.clone())).collect(); + let functions = core::mem::take(&mut self.reconstructed_functions).into_iter().collect(); ProgramScope { program_id: input.program_id, diff --git a/tests/expectations/compiler/definition/define_multiple_variables_fail.out b/tests/expectations/compiler/definition/define_multiple_variables_fail.out index 61bd94c8dd..9f2ba859c2 100644 --- a/tests/expectations/compiler/definition/define_multiple_variables_fail.out +++ b/tests/expectations/compiler/definition/define_multiple_variables_fail.out @@ -2,4 +2,4 @@ namespace: Compile expectation: Fail outputs: - - "Error [ETYC0372082]: Expected a tuple with 2 elements, found one with 3 elements\n --> compiler-test:5:13\n |\n 5 | let (a,b,c): (u8,u8) = (2u8,3u8);\n | ^^^^^^^\nError [ETYC0372082]: Expected a tuple with 3 elements, found one with 2 elements\n --> compiler-test:6:13\n |\n 6 | let (d,e): (u8,u8,u8) = (1u8,2u8,3u8);\n | ^^^^^\n" + - "Error [ETYC0372082]: Expected a tuple with 2 elements, found one with 3 elements\n --> compiler-test:5:13\n |\n 5 | let (a,b,c): (u8,u8) = (2u8,3u8);\n | ^^^^^^^\nError [ETYC0372082]: Expected a tuple with 3 elements, found one with 2 elements\n --> compiler-test:6:13\n |\n 6 | let (d,e): (u8,u8,u8) = (1u8,2u8,3u8);\n | ^^^^^\nError [ETYC0372003]: Expected type `(u8,u8,u8)` but type `u8` was found\n --> compiler-test:7:36\n |\n 7 | let (g,h,i): (u8,u8,u8) = (1u8);\n | ^^^\n" diff --git a/tests/expectations/compiler/definition/tuple_def_fail.out b/tests/expectations/compiler/definition/tuple_def_fail.out new file mode 100644 index 0000000000..f10aaec82c --- /dev/null +++ b/tests/expectations/compiler/definition/tuple_def_fail.out @@ -0,0 +1,5 @@ +--- +namespace: Compile +expectation: Fail +outputs: + - "Error [ETYC0372061]: Tuples on the left-hand side of a `DefinitionStatement` can only contain identifiers.\n --> compiler-test:5:14\n |\n 5 | let (1u8+1u8,1u8+1u8): (u8,u8) = (1u8,2u8);\n | ^^^^^^^\nError [ETYC0372061]: Tuples on the left-hand side of a `DefinitionStatement` can only contain identifiers.\n --> compiler-test:5:22\n |\n 5 | let (1u8+1u8,1u8+1u8): (u8,u8) = (1u8,2u8);\n | ^^^^^^^\n" diff --git a/tests/tests/compiler/definition/tuple_def_fail.leo b/tests/tests/compiler/definition/tuple_def_fail.leo index 7f5c56b0d1..9fe012351b 100644 --- a/tests/tests/compiler/definition/tuple_def_fail.leo +++ b/tests/tests/compiler/definition/tuple_def_fail.leo @@ -1,6 +1,6 @@ /* namespace: Compile -expectation: Pass +expectation: Fail */ program test.aleo {