This commit is contained in:
Pranav Gaddamadugu 2022-08-30 00:12:23 -07:00
parent 881ee569d1
commit 9f56b34677
19 changed files with 176 additions and 137 deletions

View File

@ -165,8 +165,8 @@ impl<'a> Compiler<'a> {
}
/// Runs the static single assignment pass.
pub fn static_single_assignment_pass(&mut self) -> Result<()> {
self.ast = StaticSingleAssigner::do_pass((std::mem::take(&mut self.ast), self.handler))?;
pub fn static_single_assignment_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
self.ast = StaticSingleAssigner::do_pass((std::mem::take(&mut self.ast), self.handler, symbol_table))?;
if self.output_options.ssa_ast {
self.write_ast_to_json("ssa_ast.json")?;
@ -184,7 +184,7 @@ impl<'a> Compiler<'a> {
let st = self.loop_unrolling_pass(st)?;
// TODO: Make this pass optional.
self.static_single_assignment_pass()?;
self.static_single_assignment_pass(&st)?;
Ok(st)
}

View File

@ -193,8 +193,8 @@ fn temp_dir() -> PathBuf {
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>, handler: &Handler) -> Result<String, LeoError> {
let st = parsed.symbol_table_pass()?;
let st = parsed.type_checker_pass(st)?;
let _st = parsed.loop_unrolling_pass(st)?;
parsed.static_single_assignment_pass()?;
let st = parsed.loop_unrolling_pass(st)?;
parsed.static_single_assignment_pass(&st)?;
// Compile Leo program to bytecode.
let bytecode = CodeGenerator::do_pass((&parsed.ast, handler))?;

View File

@ -57,17 +57,17 @@ pub(crate) use rename_table::*;
pub mod static_single_assigner;
pub use static_single_assigner::*;
use crate::Pass;
use crate::{Pass, SymbolTable};
use leo_ast::{Ast, ProgramConsumer};
use leo_errors::{emitter::Handler, Result};
impl<'a> Pass for StaticSingleAssigner<'a> {
type Input = (Ast, &'a Handler);
type Input = (Ast, &'a Handler, &'a SymbolTable);
type Output = Result<Ast>;
fn do_pass((ast, handler): Self::Input) -> Self::Output {
let mut consumer = StaticSingleAssigner::new(handler);
fn do_pass((ast, handler, st): Self::Input) -> Self::Output {
let mut consumer = StaticSingleAssigner::new(handler, st);
let program = consumer.consume_program(ast.into_repr());
handler.last_err()?;

View File

@ -15,12 +15,9 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::StaticSingleAssigner;
use itertools::Itertools;
use leo_ast::{
AccessExpression, AssociatedFunction, BinaryExpression, CallExpression, CircuitExpression,
CircuitVariableInitializer, ErrExpression, Expression, ExpressionConsumer, Identifier, Literal, MemberAccess,
Statement, TernaryExpression, TupleAccess, TupleExpression, UnaryExpression,
};
use leo_ast::{AccessExpression, AssociatedFunction, BinaryExpression, CallExpression, CircuitExpression, CircuitMember, CircuitVariableInitializer, ErrExpression, Expression, ExpressionConsumer, Identifier, Literal, MemberAccess, Statement, TernaryExpression, TupleAccess, TupleExpression, UnaryExpression};
impl ExpressionConsumer for StaticSingleAssigner<'_> {
type Output = (Expression, Vec<Statement>);
@ -155,16 +152,23 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> {
})
.collect();
// Construct and accumulate a new assignment statement for the call expression.
let (place, statement) = self.unique_simple_assign_statement(Expression::Circuit(CircuitExpression {
name: input.name,
span: input.span,
members,
}));
statements.push(statement);
// Add the variable to the set of circuit variables.
match place {
Expression::Identifier(identifier) => self.circuits.insert(identifier.name),
_ => unreachable!("`place` is always an identifier"),
};
// Note that we do not construct a new assignment statement for the tuple expression.
// Expressions that produce compound data types need to be handled separately.
(
Expression::Circuit(CircuitExpression {
name: input.name,
span: input.span,
members,
}),
statements,
)
(place, statements)
}
/// `ErrExpressions` should not exist and thus do not need to be handled.
@ -217,16 +221,79 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> {
statements.append(&mut if_true_statements);
statements.append(&mut if_false_statements);
// Construct and accumulate a unique assignment statement storing the result of the ternary expression.
let (place, statement) = self.unique_simple_assign_statement(Expression::Ternary(TernaryExpression {
condition: Box::new(cond_expr),
if_true: Box::new(if_true_expr),
if_false: Box::new(if_false_expr),
span: input.span,
}));
statements.push(statement);
match (if_true_expr, if_false_expr) {
(Expression::Tuple(first), Expression::Tuple(second)) => {
let tuple = Expression::Tuple(TupleExpression {
elements: first
.elements
.into_iter()
.zip_eq(second.elements.into_iter())
.map(|(if_true, if_false)| {
let (ternary, stmts) = self.consume_ternary(TernaryExpression {
condition: Box::new(cond_expr.clone()),
if_true: Box::new(if_true),
if_false: Box::new(if_false),
span: input.span,
});
statements.extend(stmts);
ternary
})
.collect(),
span: Default::default(),
});
(tuple, statements)
}
// If the `true` and `false` cases are circuits, handle the appropriately.
// Note that type checking guarantees that both expressions have the same same type.
(Expression::Identifier(first), Expression::Identifier(second)) if self.circuits.contains(&first.name) && self.circuits.contains(&second.name) => {
let first_circuit = self.symbol_table.lookup_circuit(first.name).unwrap();
let second_circuit = self.symbol_table.lookup_circuit(second.name).unwrap();
assert_eq!(first_circuit, second_circuit);
(place, statements)
// For each circuit member, construct a new ternary expression.
let members = first_circuit.members.iter().map(|CircuitMember::CircuitVariable(id, _)| {
let (expression, stmts) = self.consume_ternary(TernaryExpression {
condition: Box::new(cond_expr.clone()),
if_true: Box::new(Expression::Access(AccessExpression::Member(MemberAccess {
inner: Box::new(Expression::Identifier(first)),
name: *id,
span: Default::default()
}))),
if_false: Box::new(Expression::Access(AccessExpression::Member(MemberAccess {
inner: Box::new(Expression::Identifier(second)),
name: *id,
span: Default::default()
}))),
span: Default::default(),
});
statements.extend(stmts);
CircuitVariableInitializer { identifier: *id, expression: Some(expression) }
}).collect();
let (expr, stmts) = self.consume_circuit_init(CircuitExpression {
name: first_circuit.identifier,
members,
span: Default::default(),
});
statements.extend(stmts);
(expr, statements)
}
(if_true_expr, if_false_expr) => {
// Construct and accumulate a unique assignment statement storing the result of the ternary expression.
let (place, statement) = self.unique_simple_assign_statement(Expression::Ternary(TernaryExpression {
condition: Box::new(cond_expr),
if_true: Box::new(if_true_expr),
if_false: Box::new(if_false_expr),
span: input.span,
}));
statements.push(statement);
(place, statements)
}
}
}
/// Consumes a tuple expression, accumulating any statements that are generated

View File

@ -38,10 +38,13 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
// Note that this order is necessary to ensure that the right-hand-side uses the correct name when consuming a complex assignment.
// TODO: Can lhs have complex expressions?
self.is_lhs = true;
let place = self.consume_expression(assign.place).0;
let place = match self.consume_expression(assign.place).0 {
Expression::Identifier(identifier) => identifier,
_ => panic!("Type checking"),
};
self.is_lhs = false;
statements.push(Self::simple_assign_statement(place, value));
statements.push(self.simple_assign_statement(place, value));
statements
}
@ -124,18 +127,22 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
// Create a new name for the variable written to in the `ConditionalStatement`.
let new_name = self.unique_symbol(symbol);
let (value, stmts) = self.consume_ternary(TernaryExpression {
condition: Box::new(condition.clone()),
if_true: create_phi_argument(&if_table, **symbol),
if_false: create_phi_argument(&else_table, **symbol),
span: Default::default(),
});
statements.extend(stmts);
// Create a new `AssignStatement` for the phi function.
let assignment = Self::simple_assign_statement(
Expression::Identifier(Identifier {
let assignment = self.simple_assign_statement(
Identifier {
name: new_name,
span: Default::default(),
}),
Expression::Ternary(TernaryExpression {
condition: Box::new(condition.clone()),
if_true: create_phi_argument(&if_table, **symbol),
if_false: create_phi_argument(&else_table, **symbol),
span: Default::default(),
}),
},
value,
);
// Update the `RenameTable` with the new name of the variable.
@ -219,7 +226,7 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
};
self.is_lhs = false;
statements.push(Self::simple_assign_statement(Expression::Identifier(identifier), value));
statements.push(self.simple_assign_statement(identifier, value));
statements
}
@ -296,7 +303,7 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
// Consume the expression and add it to `early_returns`.
let (expression, statements) = self.consume_expression(input.expression);
// Note that this is the only place where `self.early_returns` is appended.
// Furthermore, `expression` will always be an identifier or tuple expression.
// Furthermore, `expression` will always be an identifier, tuple expression, or circuit expression.
self.early_returns.push((guard, expression));
statements

View File

@ -14,18 +14,21 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::RenameTable;
use itertools::Itertools;
use std::fmt::Display;
use crate::{RenameTable, SymbolTable};
use leo_ast::{
AssignStatement, CircuitExpression, CircuitVariableInitializer, Expression, Identifier, Statement,
TernaryExpression, TupleExpression,
AssignStatement, Expression, ExpressionConsumer, Identifier,
Statement, TernaryExpression,
};
use leo_errors::emitter::Handler;
use leo_span::Symbol;
use indexmap::IndexSet;
use std::fmt::Display;
pub struct StaticSingleAssigner<'a> {
/// The symbol table associated with the program.
pub(crate) symbol_table: &'a SymbolTable,
/// The `RenameTable` for the current basic block in the AST
pub(crate) rename_table: RenameTable,
/// An error handler used for any errors found during unrolling.
@ -34,6 +37,8 @@ pub struct StaticSingleAssigner<'a> {
pub(crate) counter: usize,
/// A flag to determine whether or not the traversal is on the left-hand side of a definition or an assignment.
pub(crate) is_lhs: bool,
/// The set of variables that are circuits.
pub(crate) circuits: IndexSet<Symbol>,
/// A stack of condition `Expression`s visited up to the current point in the AST.
pub(crate) condition_stack: Vec<Expression>,
/// A list containing tuples of guards and expressions associated with early `ReturnStatement`s.
@ -45,12 +50,14 @@ pub struct StaticSingleAssigner<'a> {
}
impl<'a> StaticSingleAssigner<'a> {
pub(crate) fn new(handler: &'a Handler) -> Self {
pub(crate) fn new(handler: &'a Handler, symbol_table: &'a SymbolTable) -> Self {
Self {
symbol_table,
rename_table: RenameTable::new(None),
_handler: handler,
counter: 0,
is_lhs: false,
circuits: IndexSet::new(),
condition_stack: Vec::new(),
early_returns: Vec::new(),
early_finalizes: Vec::new(),
@ -64,9 +71,14 @@ impl<'a> StaticSingleAssigner<'a> {
}
/// Constructs the assignment statement `place = expr;`.
pub(crate) fn simple_assign_statement(place: Expression, value: Expression) -> Statement {
/// This function should be the only place where `AssignStatement`s are constructed.
pub(crate) fn simple_assign_statement(&mut self, identifier: Identifier, value: Expression) -> Statement {
if matches!(value, Expression::Circuit(_)) {
self.circuits.insert(identifier.name);
}
Statement::Assign(Box::new(AssignStatement {
place,
place: Expression::Identifier(identifier),
value,
span: Default::default(),
}))
@ -77,14 +89,16 @@ impl<'a> StaticSingleAssigner<'a> {
pub(crate) fn unique_simple_assign_statement(&mut self, expr: Expression) -> (Expression, Statement) {
// Create a new variable for the expression.
let name = self.unique_symbol("$var");
let place = Expression::Identifier(Identifier {
let place = Identifier {
name,
span: Default::default(),
});
};
// Update the rename table.
self.rename_table.update(name, name);
(place.clone(), Self::simple_assign_statement(place, expr))
(Expression::Identifier(place), self.simple_assign_statement(place, expr))
}
/// Clears the state associated with `ReturnStatements`, returning the ones that were previously produced.
@ -120,24 +134,24 @@ impl<'a> StaticSingleAssigner<'a> {
let (_, last_expression) = guards.pop().unwrap();
// Produce a chain of ternary expressions and assignments for the guards.
let mut stmts = Vec::with_capacity(guards.len());
let mut statements = Vec::with_capacity(guards.len());
// Helper to construct and store ternary assignments. e.g `$ret$0 = $var$0 ? $var$1 : $var$2`
let mut construct_ternary_assignment = |guard: Expression, if_true: Expression, if_false: Expression| {
let place = Expression::Identifier(Identifier {
let place = Identifier {
name: self.unique_symbol(prefix),
span: Default::default(),
};
let (value, stmts) = self.consume_ternary(TernaryExpression {
condition: Box::new(guard),
if_true: Box::new(if_true),
if_false: Box::new(if_false),
span: Default::default(),
});
stmts.push(Self::simple_assign_statement(
place.clone(),
Expression::Ternary(TernaryExpression {
condition: Box::new(guard),
if_true: Box::new(if_true),
if_false: Box::new(if_false),
span: Default::default(),
}),
));
place
statements.extend(stmts);
statements.push(self.simple_assign_statement(place, value));
Expression::Identifier(place)
};
let expression = guards
@ -146,58 +160,9 @@ impl<'a> StaticSingleAssigner<'a> {
.fold(last_expression, |acc, (guard, expr)| match guard {
None => unreachable!("All expression except for the last one must have a guard."),
// Note that type checking guarantees that all expressions have the same type.
Some(guard) => match (expr, acc) {
// If the function returns tuples, fold the expressions into a tuple of ternary expressions.
// Note that `expr` and `acc` are correspond to the `if` and `else` cases of the ternary expression respectively.
(Expression::Tuple(expr_tuple), Expression::Tuple(acc_tuple)) => {
Expression::Tuple(TupleExpression {
elements: expr_tuple
.elements
.into_iter()
.zip_eq(acc_tuple.elements.into_iter())
.map(|(if_true, if_false)| {
construct_ternary_assignment(guard.clone(), if_true, if_false)
})
.collect(),
span: Default::default(),
})
}
// If the expression is a circuit, fold the expressions into a circuit of ternary expressions.
// Note that `expr` and `acc` are correspond to the `if` and `else` cases of the ternary expression respectively.
(Expression::Circuit(expr_circuit), Expression::Circuit(acc_circuit)) => {
Expression::Circuit(CircuitExpression {
name: acc_circuit.name,
span: acc_circuit.span,
members: expr_circuit
.members
.into_iter()
.zip_eq(acc_circuit.members.into_iter())
.map(|(if_true, if_false)| {
let expression = construct_ternary_assignment(
guard.clone(),
match if_true.expression {
None => Expression::Identifier(if_true.identifier),
Some(expr) => expr,
},
match if_false.expression {
None => Expression::Identifier(if_false.identifier),
Some(expr) => expr,
},
);
CircuitVariableInitializer {
identifier: if_true.identifier,
expression: Some(expression),
}
})
.collect(),
})
}
// Otherwise, fold the return expressions into a single ternary expression.
// Note that `expr` and `acc` are correspond to the `if` and `else` cases of the ternary expression respectively.
(expr, acc) => construct_ternary_assignment(guard, expr, acc),
},
Some(guard) => construct_ternary_assignment(guard, expr, acc),
});
(stmts, expression)
(statements, expression)
}
}

View File

@ -17,7 +17,6 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -35,6 +34,7 @@ outputs:
span:
lo: 30
hi: 49
finalize: ~
span:
lo: 11
hi: 49
@ -48,7 +48,6 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -66,6 +65,7 @@ outputs:
span:
lo: 99
hi: 118
finalize: ~
span:
lo: 80
hi: 118

View File

@ -25,12 +25,12 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements: []
span:
lo: 38
hi: 40
finalize: ~
span:
lo: 17
hi: 40
@ -44,12 +44,12 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements: []
span:
lo: 73
hi: 75
finalize: ~
span:
lo: 51
hi: 75
@ -63,12 +63,12 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements: []
span:
lo: 105
hi: 107
finalize: ~
span:
lo: 83
hi: 107

View File

@ -20,12 +20,12 @@ outputs:
hi: 20
output:
Integer: U8
core_mapping: ~
block:
statements: []
span:
lo: 32
hi: 34
finalize: ~
span:
lo: 2
hi: 34
@ -42,12 +42,12 @@ outputs:
hi: 57
output:
Integer: U8
core_mapping: ~
block:
statements: []
span:
lo: 70
hi: 72
finalize: ~
span:
lo: 36
hi: 72

View File

@ -27,7 +27,6 @@ outputs:
hi: 31
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -45,6 +44,7 @@ outputs:
span:
lo: 44
hi: 63
finalize: ~
span:
lo: 2
hi: 63
@ -68,7 +68,6 @@ outputs:
hi: 94
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -86,6 +85,7 @@ outputs:
span:
lo: 107
hi: 126
finalize: ~
span:
lo: 65
hi: 126

View File

@ -17,12 +17,12 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements: []
span:
lo: 107
hi: 109
finalize: ~
span:
lo: 85
hi: 109

View File

@ -13,12 +13,12 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements: []
span:
lo: 21
hi: 23
finalize: ~
span:
lo: 2
hi: 23

View File

@ -27,7 +27,6 @@ outputs:
hi: 22
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -45,6 +44,7 @@ outputs:
span:
lo: 35
hi: 54
finalize: ~
span:
lo: 2
hi: 54

View File

@ -27,7 +27,6 @@ outputs:
hi: 22
output:
Integer: U32
core_mapping: ~
block:
statements:
- Return:
@ -45,6 +44,7 @@ outputs:
span:
lo: 36
hi: 55
finalize: ~
span:
lo: 2
hi: 55

View File

@ -27,7 +27,6 @@ outputs:
hi: 29
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -45,6 +44,7 @@ outputs:
span:
lo: 42
hi: 61
finalize: ~
span:
lo: 2
hi: 61
@ -68,7 +68,6 @@ outputs:
hi: 90
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -86,6 +85,7 @@ outputs:
span:
lo: 103
hi: 122
finalize: ~
span:
lo: 63
hi: 122

View File

@ -13,7 +13,6 @@ outputs:
input: []
output:
Integer: U32
core_mapping: ~
block:
statements:
- Return:
@ -31,6 +30,7 @@ outputs:
span:
lo: 22
hi: 41
finalize: ~
span:
lo: 2
hi: 41

View File

@ -2,4 +2,4 @@
namespace: Parse
expectation: Fail
outputs:
- "Error [EPAR0370005]: expected -> -- found '{'\n --> test:3:17\n |\n 3 | function main() {\n | ^"
- "Error [EPAR0370005]: expected : -- found '='\n --> test:4:11\n |\n 4 | let x = 1u8;\n | ^"

View File

@ -13,7 +13,6 @@ outputs:
input: []
output:
Integer: U8
core_mapping: ~
block:
statements:
- Return:
@ -30,5 +29,6 @@ outputs:
- U8
- "1"
op: Add
finalize: ~
circuits: {}
mappings: {}

View File

@ -2,4 +2,4 @@
namespace: Parse
expectation: Fail
outputs:
- "Error [EPAR0370005]: expected -> -- found '{'\n --> test:3:16\n |\n 3 | function foo() {\n | ^"
- "Error [EPAR0370005]: expected : -- found 'x'\n --> test:4:13\n |\n 4 | let mut x = 0;\n | ^"