Update compiler pipelines

This commit is contained in:
Pranav Gaddamadugu 2023-08-17 16:22:33 -04:00
parent 2fe6892645
commit 661a7cd508
6 changed files with 54 additions and 41 deletions

View File

@ -17,8 +17,8 @@
//! The compiler for Leo programs.
//!
//! The [`Compiler`] type compiles Leo programs into R1CS circuits.
use leo_ast::Program;
pub use leo_ast::{Ast, InputAst};
use leo_ast::{NodeBuilder, Program};
use leo_errors::{emitter::Handler, CompilerError, Result};
pub use leo_passes::SymbolTable;
use leo_passes::*;
@ -48,6 +48,10 @@ pub struct Compiler<'a> {
pub input_ast: Option<InputAst>,
/// Options configuring compilation.
compiler_options: CompilerOptions,
/// The `NodeCounter` used to generate sequentially increasing `NodeID`s.
node_builder: NodeBuilder,
/// The `Assigner` is used to construct (unique) assignment statements.
assigner: Assigner,
}
impl<'a> Compiler<'a> {
@ -60,6 +64,8 @@ impl<'a> Compiler<'a> {
output_directory: PathBuf,
compiler_options: Option<CompilerOptions>,
) -> Self {
let node_builder = NodeBuilder::default();
let assigner = Assigner::default();
Self {
handler,
main_file_path,
@ -69,6 +75,8 @@ impl<'a> Compiler<'a> {
ast: Ast::new(Program::default()),
input_ast: None,
compiler_options: compiler_options.unwrap_or_default(),
node_builder,
assigner,
}
}
@ -92,7 +100,7 @@ impl<'a> Compiler<'a> {
let prg_sf = with_session_globals(|s| s.source_map.new_source(program_string, name));
// Use the parser to construct the abstract syntax tree (ast).
self.ast = leo_parser::parse_ast(self.handler, &prg_sf.src, prg_sf.start_pos)?;
self.ast = leo_parser::parse_ast(self.handler, &self.node_builder, &prg_sf.src, prg_sf.start_pos)?;
// If the program is imported, then check that the name of its program scope matches the file name.
// Note that parsing enforces that there is exactly one program scope in a file.
@ -132,7 +140,8 @@ impl<'a> Compiler<'a> {
.map_err(|e| CompilerError::file_read_error(&input_file_path, e))?;
// Parse and serialize it.
let input_ast = leo_parser::parse_input(self.handler, &input_sf.src, input_sf.start_pos)?;
let input_ast =
leo_parser::parse_input(self.handler, &self.node_builder, &input_sf.src, input_sf.start_pos)?;
if self.compiler_options.output.initial_ast {
// Write the input AST snapshot post parsing.
if self.compiler_options.output.spans_enabled {
@ -166,7 +175,8 @@ impl<'a> Compiler<'a> {
/// Runs the loop unrolling pass.
pub fn loop_unrolling_pass(&mut self, symbol_table: SymbolTable) -> Result<SymbolTable> {
let (ast, symbol_table) = Unroller::do_pass((std::mem::take(&mut self.ast), self.handler, symbol_table))?;
let (ast, symbol_table) =
Unroller::do_pass((std::mem::take(&mut self.ast), self.handler, &self.node_builder, symbol_table))?;
self.ast = ast;
if self.compiler_options.output.unrolled_ast {
@ -177,45 +187,50 @@ impl<'a> Compiler<'a> {
}
/// Runs the static single assignment pass.
pub fn static_single_assignment_pass(&mut self, symbol_table: &SymbolTable) -> Result<Assigner> {
let (ast, assigner) = StaticSingleAssigner::do_pass((std::mem::take(&mut self.ast), symbol_table))?;
self.ast = ast;
pub fn static_single_assignment_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
self.ast = StaticSingleAssigner::do_pass((
std::mem::take(&mut self.ast),
&self.node_builder,
&self.assigner,
symbol_table,
))?;
if self.compiler_options.output.ssa_ast {
self.write_ast_to_json("ssa_ast.json")?;
}
Ok(assigner)
Ok(())
}
/// Runs the flattening pass.
pub fn flattening_pass(&mut self, symbol_table: &SymbolTable, assigner: Assigner) -> Result<Assigner> {
let (ast, assigner) = Flattener::do_pass((std::mem::take(&mut self.ast), symbol_table, assigner))?;
self.ast = ast;
pub fn flattening_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
self.ast =
Flattener::do_pass((std::mem::take(&mut self.ast), symbol_table, &self.node_builder, &self.assigner))?;
if self.compiler_options.output.flattened_ast {
self.write_ast_to_json("flattened_ast.json")?;
}
Ok(assigner)
Ok(())
}
/// Runs the function inlining pass.
pub fn function_inlining_pass(&mut self, call_graph: &CallGraph, assigner: Assigner) -> Result<Assigner> {
let (ast, assigner) = FunctionInliner::do_pass((std::mem::take(&mut self.ast), call_graph, assigner))?;
pub fn function_inlining_pass(&mut self, call_graph: &CallGraph) -> Result<()> {
let ast =
FunctionInliner::do_pass((std::mem::take(&mut self.ast), &self.node_builder, call_graph, &self.assigner))?;
self.ast = ast;
if self.compiler_options.output.inlined_ast {
self.write_ast_to_json("inlined_ast.json")?;
}
Ok(assigner)
Ok(())
}
/// Runs the dead code elimination pass.
pub fn dead_code_elimination_pass(&mut self) -> Result<()> {
if self.compiler_options.build.dce_enabled {
self.ast = DeadCodeEliminator::do_pass(std::mem::take(&mut self.ast))?;
self.ast = DeadCodeEliminator::do_pass((std::mem::take(&mut self.ast), &self.node_builder))?;
}
if self.compiler_options.output.dce_ast {
@ -243,12 +258,11 @@ impl<'a> Compiler<'a> {
// TODO: Make this pass optional.
let st = self.loop_unrolling_pass(st)?;
// TODO: Make this pass optional.
let assigner = self.static_single_assignment_pass(&st)?;
self.static_single_assignment_pass(&st)?;
let assigner = self.flattening_pass(&st, assigner)?;
self.flattening_pass(&st)?;
let _ = self.function_inlining_pass(&call_graph, assigner)?;
self.function_inlining_pass(&call_graph)?;
self.dead_code_elimination_pass()?;

View File

@ -148,7 +148,7 @@ pub fn parse_program<'a>(
let name = cwd.map_or_else(|| FileName::Custom("compiler-test".into()), FileName::Real);
compiler.parse_program_from_string(program_string, name)?;
CheckUniqueNodeIds::new().run(&mut compiler.ast)?;
CheckUniqueNodeIds::new().visit_program(&compiler.ast.ast);
Ok(compiler)
}

View File

@ -30,8 +30,6 @@ pub struct FunctionInliner<'a> {
pub(crate) assignment_renamer: AssignmentRenamer<'a>,
/// A map of reconstructed functions in the current program scope.
pub(crate) reconstructed_functions: IndexMap<Symbol, Function>,
/// Whether or not we are currently inlining a function.
pub(crate) inlining: bool,
}
impl<'a> FunctionInliner<'a> {
@ -42,7 +40,6 @@ impl<'a> FunctionInliner<'a> {
call_graph,
assignment_renamer: AssignmentRenamer::new(assigner),
reconstructed_functions: Default::default(),
inlining: false,
}
}
}

View File

@ -21,7 +21,6 @@ use leo_ast::{
Expression,
ExpressionReconstructor,
Identifier,
NodeID,
ReturnStatement,
Statement,
StatementReconstructor,

View File

@ -16,7 +16,7 @@
use super::*;
use leo_ast::Struct;
use leo_ast::{NodeBuilder, Struct};
use leo_compiler::{Compiler, CompilerOptions, InputAst, OutputOptions};
use leo_package::{
build::BuildDirectory,
@ -103,6 +103,9 @@ impl Command for Build {
// Initialize error handler
let handler = Handler::default();
// Initialize a node counter.
let node_builder = NodeBuilder::default();
// Fetch paths to all .leo files in the source directory.
let source_files = SourceDirectory::files(&package_path)?;
@ -158,7 +161,7 @@ impl Command for Build {
.map_err(|e| CompilerError::file_read_error(&input_file_path, e))?;
// TODO: This is a hack to notify the user that something is wrong with the input file. Redesign.
leo_parser::parse_input(&handler, &input_sf.src, input_sf.start_pos)
leo_parser::parse_input(&handler, &node_builder, &input_sf.src, input_sf.start_pos)
.map_err(|_e| println!("Warning: Failed to parse input file"))
.ok()
} else {

View File

@ -216,9 +216,9 @@ impl Sample {
let (symbol_table, _struct_graph, _call_graph) =
compiler.type_checker_pass(symbol_table).expect("failed to run type check pass");
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
let assigner = compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
let start = Instant::now();
let out = compiler.flattening_pass(&symbol_table, assigner);
let out = compiler.flattening_pass(&symbol_table);
let time = start.elapsed();
out.expect("failed to run flattener pass");
time
@ -231,10 +231,10 @@ impl Sample {
let (symbol_table, _struct_graph, call_graph) =
compiler.type_checker_pass(symbol_table).expect("failed to run type check pass");
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
let assigner = compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
let assigner = compiler.flattening_pass(&symbol_table, assigner).expect("failed to run flattener pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass");
let start = Instant::now();
let out = compiler.function_inlining_pass(&call_graph, assigner);
let out = compiler.function_inlining_pass(&call_graph);
let time = start.elapsed();
out.expect("failed to run inliner pass");
time
@ -247,9 +247,9 @@ impl Sample {
let (symbol_table, _struct_graph, call_graph) =
compiler.type_checker_pass(symbol_table).expect("failed to run type check pass");
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
let assigner = compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
let assigner = compiler.flattening_pass(&symbol_table, assigner).expect("failed to run flattener pass");
let _ = compiler.function_inlining_pass(&call_graph, assigner).expect("failed to run inliner pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass");
compiler.function_inlining_pass(&call_graph).expect("failed to run inliner pass");
let start = Instant::now();
let out = compiler.dead_code_elimination_pass();
let time = start.elapsed();
@ -264,9 +264,9 @@ impl Sample {
let (symbol_table, struct_graph, call_graph) =
compiler.type_checker_pass(symbol_table).expect("failed to run type check pass");
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
let assigner = compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
let assigner = compiler.flattening_pass(&symbol_table, assigner).expect("failed to run flattener pass");
let _ = compiler.function_inlining_pass(&call_graph, assigner).expect("failed to run inliner pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass");
compiler.function_inlining_pass(&call_graph).expect("failed to run inliner pass");
compiler.dead_code_elimination_pass().expect("failed to run dce pass");
let start = Instant::now();
let out = compiler.code_generation_pass(&symbol_table, &struct_graph, &call_graph);
@ -285,9 +285,9 @@ impl Sample {
let (symbol_table, struct_graph, call_graph) =
compiler.type_checker_pass(symbol_table).expect("failed to run type check pass");
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
let assigner = compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
let assigner = compiler.flattening_pass(&symbol_table, assigner).expect("failed to run flattening pass");
compiler.function_inlining_pass(&call_graph, assigner).expect("failed to run function inlining pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattening pass");
compiler.function_inlining_pass(&call_graph).expect("failed to run function inlining pass");
compiler.dead_code_elimination_pass().expect("failed to run dce pass");
compiler
.code_generation_pass(&symbol_table, &struct_graph, &call_graph)