Implement post-ordering for functions during code-generation

This commit is contained in:
d0cd 2022-11-16 00:57:56 -08:00
parent f06b83c7f4
commit e633c62b71
6 changed files with 32 additions and 22 deletions

View File

@ -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.

View 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)
} }

View File

@ -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(),

View File

@ -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)

View File

@ -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);

View File

@ -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