Skip to content

Commit

Permalink
Update codegen, fix compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
d0cd committed Apr 19, 2024
1 parent 0b07ea6 commit eb18118
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 7 deletions.
4 changes: 4 additions & 0 deletions compiler/compiler/tests/utilities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,12 @@ pub fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<String, L

parsed.function_inlining_pass(&call_graph)?;

println!("AST after inlining: {:#?}", parsed.ast.ast);

parsed.dead_code_elimination_pass()?;

println!("AST after DCE: {:#?}", parsed.ast.ast);

// Compile Leo program to bytecode.
let bytecode = parsed.code_generation_pass(&st, &struct_graph, &call_graph)?;

Expand Down
3 changes: 3 additions & 0 deletions compiler/passes/src/code_generation/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub struct CodeGenerator<'a> {
pub(crate) program: &'a Program,
// The program ID of the current program.
pub(crate) program_id: Option<ProgramId>,
/// A counter to track the next available label.
pub(crate) next_label: u64,
}

impl<'a> CodeGenerator<'a> {
Expand Down Expand Up @@ -80,6 +82,7 @@ impl<'a> CodeGenerator<'a> {
futures: Vec::new(),
program,
program_id: None,
next_label: 0u64,
}
}
}
46 changes: 43 additions & 3 deletions compiler/passes/src/code_generation/visit_statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,52 @@ impl<'a> CodeGenerator<'a> {
}

fn visit_conditional(&mut self, _input: &'a ConditionalStatement) -> String {
// TODO: Once SSA is made optional, create a Leo error informing the user to enable the SSA pass.
unreachable!("`ConditionalStatement`s should not be in the AST at this phase of compilation.")
if !self.in_finalize {
unreachable!("`ConditionalStatement`s should not be in the AST at this phase of compilation.")
} else {
// Construct a label for the end of the `then` block.
let end_then_label = format!("end_then_{}", self.next_label);
self.next_label += 1;
// Construct a label for the end of the `otherwise` block if it exists.
let (has_otherwise, end_otherwise_label) = {
match _input.otherwise.is_some() {
true => {
// Construct a label for the end of the `otherwise` block.
let end_otherwise_label = { format!("end_otherwise_{}", self.next_label) };
self.next_label += 1;
(true, end_otherwise_label)
}
false => (false, String::new()),
}
};

// Create a `branch` instruction.
let (condition, mut instructions) = self.visit_expression(&_input.condition);
instructions.push_str(&format!(" branch.eq {condition} false to {end_then_label};\n"));

// Visit the `then` block.
instructions.push_str(&self.visit_block(&_input.then));
// If the `otherwise` block is present, add a branch instruction to jump to the end of the `otherwise` block.
if has_otherwise {
instructions.push_str(&format!(" branch.eq true true to {end_otherwise_label};\n"));
}

// Add a label for the end of the `then` block.
instructions.push_str(&format!(" position {};\n", end_then_label));

// Visit the `otherwise` block.
if let Some(else_block) = &_input.otherwise {
// Visit the `otherwise` block.
instructions.push_str(&self.visit_statement(else_block));
// Add a label for the end of the `otherwise` block.
instructions.push_str(&format!(" position {end_otherwise_label};\n"));
}

instructions
}
}

fn visit_iteration(&mut self, _input: &'a IterationStatement) -> String {
// TODO: Once loop unrolling is made optional, create a Leo error informing the user to enable the loop unrolling pass..
unreachable!("`IterationStatement`s should not be in the AST at this phase of compilation.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ pub struct DeadCodeEliminator<'a> {
pub(crate) used_variables: IndexSet<Symbol>,
/// Whether or not the variables are necessary.
pub(crate) is_necessary: bool,
/// Whether or not we are currently traversing a finalize block.
pub(crate) is_finalize: bool,
}

impl<'a> DeadCodeEliminator<'a> {
/// Initializes a new `DeadCodeEliminator`.
pub fn new(node_builder: &'a NodeBuilder) -> Self {
Self { node_builder, used_variables: Default::default(), is_necessary: false }
Self { node_builder, used_variables: Default::default(), is_necessary: false, is_finalize: false }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ impl ProgramReconstructor for DeadCodeEliminator<'_> {
self.used_variables.clear();
self.is_necessary = false;

// Set the `is_finalize` flag.
self.is_finalize = true;

// Traverse the finalize block.
let block = self.reconstruct_block(finalize.block).0;

// Reset the `is_finalize` flag.
self.is_finalize = false;

Finalize {
identifier: finalize.identifier,
input: finalize.input,
Expand Down
25 changes: 23 additions & 2 deletions compiler/passes/src/dead_code_elimination/eliminate_statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,29 @@ impl StatementReconstructor for DeadCodeEliminator<'_> {
}

/// Flattening removes conditional statements from the program.
fn reconstruct_conditional(&mut self, _: ConditionalStatement) -> (Statement, Self::AdditionalOutput) {
unreachable!("`ConditionalStatement`s should not be in the AST at this phase of compilation.")
fn reconstruct_conditional(&mut self, input: ConditionalStatement) -> (Statement, Self::AdditionalOutput) {
if !self.is_finalize {
unreachable!("`ConditionalStatement`s should not be in the AST at this phase of compilation.")
} else {
(
Statement::Conditional(ConditionalStatement {
then: self.reconstruct_block(input.then).0,
otherwise: input.otherwise.map(|n| Box::new(self.reconstruct_statement(*n).0)),
condition: {
// Set the `is_necessary` flag.
self.is_necessary = true;
let condition = self.reconstruct_expression(input.condition).0;
// Unset the `is_necessary` flag.
self.is_necessary = false;

condition
},
span: input.span,
id: input.id,
}),
Default::default(),
)
}
}

/// Parsing guarantees that console statements are not present in the program.
Expand Down
7 changes: 6 additions & 1 deletion compiler/passes/src/type_checking/check_statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {

// If the variable exists and its in a finalize, then check that it is in the current scope.
if self.is_finalize
&& self.is_conditional
&& self
.symbol_table
.borrow()
.lookup_variable_in_current_scope(Location::new(None, var_name.name))
.is_none()
{
self.emit_err(TypeCheckerError::finalize_cannot_assign_to_outer_scope(var_name, var.span));
self.emit_err(TypeCheckerError::finalize_cannot_assign_outside_conditional(var_name, var.span));
}

Some(var.type_.clone())
Expand Down Expand Up @@ -125,6 +126,8 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
let previous_has_return = core::mem::replace(&mut self.has_return, then_block_has_return);
// Set the `has_finalize` flag for the then-block.
let previous_has_finalize = core::mem::replace(&mut self.has_finalize, then_block_has_finalize);
// Set the `is_conditional` flag.
let previous_is_conditional = core::mem::replace(&mut self.is_conditional, true);

self.visit_block(&input.then);

Expand Down Expand Up @@ -158,6 +161,8 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
self.has_return = previous_has_return || (then_block_has_return && otherwise_block_has_return);
// Restore the previous `has_finalize` flag.
self.has_finalize = previous_has_finalize || (then_block_has_finalize && otherwise_block_has_finalize);
// Restore the previous `is_conditional` flag.
self.is_conditional = previous_is_conditional;
}

fn visit_console(&mut self, _: &'a ConsoleStatement) {
Expand Down
3 changes: 3 additions & 0 deletions compiler/passes/src/type_checking/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ pub struct TypeChecker<'a> {
pub(crate) is_stub: bool,
/// The set of used composites.
pub(crate) used_structs: IndexSet<Symbol>,
/// Whether or not we are currently traversing a conditional statement.
pub(crate) is_conditional: bool,
}

const ADDRESS_TYPE: Type = Type::Address;
Expand Down Expand Up @@ -138,6 +140,7 @@ impl<'a> TypeChecker<'a> {
program_name: None,
is_stub: true,
used_structs: IndexSet::new(),
is_conditional: false,
}
}

Expand Down

0 comments on commit eb18118

Please sign in to comment.