mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-23 18:21:38 +03:00
Implement post-ordering for functions during code-generation
This commit is contained in:
parent
f06b83c7f4
commit
e633c62b71
@ -163,7 +163,7 @@ impl<'a> Compiler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the type checker pass.
|
/// Runs the type checker pass.
|
||||||
pub fn type_checker_pass(&'a self, symbol_table: SymbolTable) -> Result<(SymbolTable, StructGraph)> {
|
pub fn type_checker_pass(&'a self, symbol_table: SymbolTable) -> Result<(SymbolTable, StructGraph, CallGraph)> {
|
||||||
TypeChecker::do_pass((&self.ast, self.handler, symbol_table))
|
TypeChecker::do_pass((&self.ast, self.handler, symbol_table))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,9 +203,9 @@ impl<'a> Compiler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the compiler stages.
|
/// Runs the compiler stages.
|
||||||
pub fn compiler_stages(&mut self) -> Result<(SymbolTable, StructGraph)> {
|
pub fn compiler_stages(&mut self) -> Result<(SymbolTable, StructGraph, CallGraph)> {
|
||||||
let st = self.symbol_table_pass()?;
|
let st = self.symbol_table_pass()?;
|
||||||
let (st, struct_graph) = self.type_checker_pass(st)?;
|
let (st, struct_graph, call_graph) = self.type_checker_pass(st)?;
|
||||||
|
|
||||||
// TODO: Make this pass optional.
|
// TODO: Make this pass optional.
|
||||||
let st = self.loop_unrolling_pass(st)?;
|
let st = self.loop_unrolling_pass(st)?;
|
||||||
@ -215,16 +215,16 @@ impl<'a> Compiler<'a> {
|
|||||||
|
|
||||||
self.flattening_pass(&st, assigner)?;
|
self.flattening_pass(&st, assigner)?;
|
||||||
|
|
||||||
Ok((st, struct_graph))
|
Ok((st, struct_graph, call_graph))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a compiled Leo program and prints the resulting bytecode.
|
/// Returns a compiled Leo program and prints the resulting bytecode.
|
||||||
// TODO: Remove when code generation is ready to be integrated into the compiler.
|
// TODO: Remove when code generation is ready to be integrated into the compiler.
|
||||||
pub fn compile_and_generate_instructions(&mut self) -> Result<(SymbolTable, String)> {
|
pub fn compile_and_generate_instructions(&mut self) -> Result<(SymbolTable, String)> {
|
||||||
self.parse_program()?;
|
self.parse_program()?;
|
||||||
let (symbol_table, struct_graph) = self.compiler_stages()?;
|
let (symbol_table, struct_graph, call_graph) = self.compiler_stages()?;
|
||||||
|
|
||||||
let bytecode = CodeGenerator::do_pass((&self.ast, &symbol_table, &struct_graph))?;
|
let bytecode = CodeGenerator::do_pass((&self.ast, &symbol_table.handler, &struct_graph, &call_graph))?;
|
||||||
|
|
||||||
Ok((symbol_table, bytecode))
|
Ok((symbol_table, bytecode))
|
||||||
}
|
}
|
||||||
@ -232,7 +232,7 @@ impl<'a> Compiler<'a> {
|
|||||||
/// Returns a compiled Leo program.
|
/// Returns a compiled Leo program.
|
||||||
pub fn compile(&mut self) -> Result<SymbolTable> {
|
pub fn compile(&mut self) -> Result<SymbolTable> {
|
||||||
self.parse_program()?;
|
self.parse_program()?;
|
||||||
self.compiler_stages().map(|(st, _)| st)
|
self.compiler_stages().map(|(st, _, _)| st)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the AST to a JSON file.
|
/// Writes the AST to a JSON file.
|
||||||
|
@ -194,14 +194,14 @@ fn temp_dir() -> PathBuf {
|
|||||||
|
|
||||||
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<String, LeoError> {
|
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<String, LeoError> {
|
||||||
let st = parsed.symbol_table_pass()?;
|
let st = parsed.symbol_table_pass()?;
|
||||||
let (st, struct_graph) = parsed.type_checker_pass(st)?;
|
let (st, struct_graph, call_graph) = parsed.type_checker_pass(st)?;
|
||||||
let st = parsed.loop_unrolling_pass(st)?;
|
let st = parsed.loop_unrolling_pass(st)?;
|
||||||
let assigner = parsed.static_single_assignment_pass(&st)?;
|
let assigner = parsed.static_single_assignment_pass(&st)?;
|
||||||
|
|
||||||
parsed.flattening_pass(&st, assigner)?;
|
parsed.flattening_pass(&st, assigner)?;
|
||||||
|
|
||||||
// Compile Leo program to bytecode.
|
// Compile Leo program to bytecode.
|
||||||
let bytecode = CodeGenerator::do_pass((&parsed.ast, &st, &struct_graph))?;
|
let bytecode = CodeGenerator::do_pass((&parsed.ast, &st, &struct_graph, &call_graph))?;
|
||||||
|
|
||||||
Ok(bytecode)
|
Ok(bytecode)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::SymbolTable;
|
use crate::SymbolTable;
|
||||||
use crate::StructGraph;
|
use crate::{CallGraph, StructGraph};
|
||||||
|
|
||||||
use leo_ast::Function;
|
use leo_ast::Function;
|
||||||
use leo_span::Symbol;
|
use leo_span::Symbol;
|
||||||
@ -27,6 +27,8 @@ pub struct CodeGenerator<'a> {
|
|||||||
pub(crate) symbol_table: &'a SymbolTable,
|
pub(crate) symbol_table: &'a SymbolTable,
|
||||||
/// The struct dependency graph for the program.
|
/// The struct dependency graph for the program.
|
||||||
pub(crate) struct_graph: &'a StructGraph,
|
pub(crate) struct_graph: &'a StructGraph,
|
||||||
|
/// The call graph for the program.
|
||||||
|
pub(crate) call_graph: &'a CallGraph,
|
||||||
/// A counter to track the next available register.
|
/// A counter to track the next available register.
|
||||||
pub(crate) next_register: u64,
|
pub(crate) next_register: u64,
|
||||||
/// Reference to the current function.
|
/// Reference to the current function.
|
||||||
@ -45,11 +47,12 @@ pub struct CodeGenerator<'a> {
|
|||||||
|
|
||||||
impl<'a> CodeGenerator<'a> {
|
impl<'a> CodeGenerator<'a> {
|
||||||
/// Initializes a new `CodeGenerator`.
|
/// Initializes a new `CodeGenerator`.
|
||||||
pub fn new(symbol_table: &'a SymbolTable, struct_graph: &'a StructGraph) -> Self {
|
pub fn new(symbol_table: &'a SymbolTable, struct_graph: &'a StructGraph, call_graph: &'a CallGraph) -> Self {
|
||||||
// Initialize variable mapping.
|
// Initialize variable mapping.
|
||||||
Self {
|
Self {
|
||||||
symbol_table,
|
symbol_table,
|
||||||
struct_graph,
|
struct_graph,
|
||||||
|
call_graph,
|
||||||
next_register: 0,
|
next_register: 0,
|
||||||
current_function: None,
|
current_function: None,
|
||||||
variable_mapping: IndexMap::new(),
|
variable_mapping: IndexMap::new(),
|
||||||
|
@ -25,18 +25,18 @@ mod visit_statements;
|
|||||||
|
|
||||||
mod visit_type;
|
mod visit_type;
|
||||||
|
|
||||||
use crate::{Pass, SymbolTable};
|
use crate::{SymbolTable};
|
||||||
use crate::{StructGraph};
|
use crate::{CallGraph, Pass, StructGraph};
|
||||||
|
|
||||||
use leo_ast::Ast;
|
use leo_ast::Ast;
|
||||||
use leo_errors::Result;
|
use leo_errors::Result;
|
||||||
|
|
||||||
impl<'a> Pass for CodeGenerator<'a> {
|
impl<'a> Pass for CodeGenerator<'a> {
|
||||||
type Input = (&'a Ast, &'a SymbolTable, &'a StructGraph);
|
type Input = (&'a Ast, &'a SymbolTable, &'a StructGraph, &'a CallGraph);
|
||||||
type Output = Result<String>;
|
type Output = Result<String>;
|
||||||
|
|
||||||
fn do_pass((ast, symbol_table, struct_graph): Self::Input) -> Self::Output {
|
fn do_pass((ast, symbol_table, struct_graph, call_graph): Self::Input) -> Self::Output {
|
||||||
let mut generator = Self::new(symbol_table, struct_graph);
|
let mut generator = Self::new(symbol_table, struct_graph, call_graph);
|
||||||
let bytecode = generator.visit_program(ast.as_repr());
|
let bytecode = generator.visit_program(ast.as_repr());
|
||||||
|
|
||||||
Ok(bytecode)
|
Ok(bytecode)
|
||||||
|
@ -85,8 +85,15 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
let mut closures = String::new();
|
let mut closures = String::new();
|
||||||
let mut functions = String::new();
|
let mut functions = String::new();
|
||||||
|
|
||||||
// Visit each `Function` in the Leo AST and produce Aleo instructions.
|
// Get the post-order ordering of the call graph.
|
||||||
program_scope.functions.values().for_each(|function| {
|
// Note that the unwrap is safe since type checking guarantees that the call graph is acyclic.
|
||||||
|
let order = self.call_graph.post_order().unwrap();
|
||||||
|
|
||||||
|
// Visit each function in the post-ordering and produce an Aleo function.
|
||||||
|
order.into_iter().for_each(|function_name| {
|
||||||
|
// Note that this unwrap is safe since type checking guarantees that all functions are declared.
|
||||||
|
let function = program_scope.functions.get(&function_name).unwrap();
|
||||||
|
|
||||||
self.is_transition_function = matches!(function.call_type, CallType::Transition);
|
self.is_transition_function = matches!(function.call_type, CallType::Transition);
|
||||||
|
|
||||||
let function_string = self.visit_function(function);
|
let function_string = self.visit_function(function);
|
||||||
|
@ -171,7 +171,7 @@ impl Sample {
|
|||||||
fn bench_loop_unroller(&self, c: &mut Criterion) {
|
fn bench_loop_unroller(&self, c: &mut Criterion) {
|
||||||
self.bencher_after_parse(c, "loop unrolling pass", |mut compiler| {
|
self.bencher_after_parse(c, "loop unrolling pass", |mut compiler| {
|
||||||
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
||||||
let (symbol_table, _struct_graph) = compiler
|
let (symbol_table, _struct_graph, _call_graph) = compiler
|
||||||
.type_checker_pass(symbol_table)
|
.type_checker_pass(symbol_table)
|
||||||
.expect("failed to run type check pass");
|
.expect("failed to run type check pass");
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
@ -185,7 +185,7 @@ impl Sample {
|
|||||||
fn bench_ssa(&self, c: &mut Criterion) {
|
fn bench_ssa(&self, c: &mut Criterion) {
|
||||||
self.bencher_after_parse(c, "full", |mut compiler| {
|
self.bencher_after_parse(c, "full", |mut compiler| {
|
||||||
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
||||||
let (symbol_table, _struct_graph) = compiler
|
let (symbol_table, _struct_graph, _call_graph) = compiler
|
||||||
.type_checker_pass(symbol_table)
|
.type_checker_pass(symbol_table)
|
||||||
.expect("failed to run type check pass");
|
.expect("failed to run type check pass");
|
||||||
let symbol_table = compiler
|
let symbol_table = compiler
|
||||||
@ -202,7 +202,7 @@ impl Sample {
|
|||||||
fn bench_flattener(&self, c: &mut Criterion) {
|
fn bench_flattener(&self, c: &mut Criterion) {
|
||||||
self.bencher_after_parse(c, "flattener pass", |mut compiler| {
|
self.bencher_after_parse(c, "flattener pass", |mut compiler| {
|
||||||
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
||||||
let (symbol_table, _struct_graph) = compiler
|
let (symbol_table, _struct_graph, _call_graph) = compiler
|
||||||
.type_checker_pass(symbol_table)
|
.type_checker_pass(symbol_table)
|
||||||
.expect("failed to run type check pass");
|
.expect("failed to run type check pass");
|
||||||
let symbol_table = compiler
|
let symbol_table = compiler
|
||||||
@ -227,7 +227,7 @@ impl Sample {
|
|||||||
.parse_program_from_string(input, name)
|
.parse_program_from_string(input, name)
|
||||||
.expect("Failed to parse program");
|
.expect("Failed to parse program");
|
||||||
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
|
||||||
let (symbol_table, _struct_graph) = compiler
|
let (symbol_table, _struct_graph, _call_graph) = compiler
|
||||||
.type_checker_pass(symbol_table)
|
.type_checker_pass(symbol_table)
|
||||||
.expect("failed to run type check pass");
|
.expect("failed to run type check pass");
|
||||||
let symbol_table = compiler
|
let symbol_table = compiler
|
||||||
|
Loading…
Reference in New Issue
Block a user