Support codegen for arrays

This commit is contained in:
Pranav Gaddamadugu 2023-10-13 16:10:00 -04:00 committed by Pranav Gaddamadugu
parent c80aee091a
commit 49a0c7a469
10 changed files with 66 additions and 71 deletions

View File

@ -243,10 +243,9 @@ impl<'a> Compiler<'a> {
}
/// Runs the destructuring pass.
pub fn destructuring_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
pub fn destructuring_pass(&mut self) -> Result<()> {
self.ast = Destructurer::do_pass((
std::mem::take(&mut self.ast),
symbol_table,
&self.type_table,
&self.node_builder,
&self.assigner,
@ -297,7 +296,7 @@ impl<'a> Compiler<'a> {
struct_graph: &StructGraph,
call_graph: &CallGraph,
) -> Result<String> {
CodeGenerator::do_pass((&self.ast, symbol_table, struct_graph, call_graph, &self.ast.ast))
CodeGenerator::do_pass((&self.ast, symbol_table, &self.type_table, struct_graph, call_graph, &self.ast.ast))
}
/// Runs the compiler stages.
@ -312,7 +311,7 @@ impl<'a> Compiler<'a> {
self.flattening_pass(&st)?;
self.destructuring_pass(&st)?;
self.destructuring_pass()?;
self.function_inlining_pass(&call_graph)?;

View File

@ -24,7 +24,6 @@ use leo_errors::{
LeoWarning,
};
use leo_package::root::env::Env;
use leo_passes::{CodeGenerator, Pass};
use leo_span::source_map::FileName;
use leo_test_framework::{test::TestConfig, Test};
@ -237,14 +236,14 @@ pub fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<String, L
parsed.flattening_pass(&st)?;
parsed.destructuring_pass(&st)?;
parsed.destructuring_pass()?;
parsed.function_inlining_pass(&call_graph)?;
parsed.dead_code_elimination_pass()?;
// Compile Leo program to bytecode.
let bytecode = CodeGenerator::do_pass((&parsed.ast, &st, &struct_graph, &call_graph, &parsed.ast.ast))?;
let bytecode = parsed.code_generation_pass(&st, &struct_graph, &call_graph)?;
Ok(bytecode)
}

View File

@ -14,16 +14,19 @@
// 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::{CallGraph, StructGraph, SymbolTable};
use crate::{CallGraph, StructGraph, SymbolTable, TypeTable};
use leo_ast::{Function, Program, ProgramId};
use leo_span::Symbol;
use indexmap::IndexMap;
use snarkvm_console::program::Access;
pub struct CodeGenerator<'a> {
/// The symbol table for the program.
pub(crate) symbol_table: &'a SymbolTable,
/// A mapping between expressions and their types.
pub(crate) type_table: &'a TypeTable,
/// The struct dependency graph for the program.
pub(crate) struct_graph: &'a StructGraph,
/// The call graph for the program.
@ -57,6 +60,7 @@ impl<'a> CodeGenerator<'a> {
/// Initializes a new `CodeGenerator`.
pub fn new(
symbol_table: &'a SymbolTable,
type_table: &'a TypeTable,
struct_graph: &'a StructGraph,
_call_graph: &'a CallGraph,
program: &'a Program,
@ -64,6 +68,7 @@ impl<'a> CodeGenerator<'a> {
// Initialize variable mapping.
Self {
symbol_table,
type_table,
struct_graph,
_call_graph,
next_register: 0,

View File

@ -25,17 +25,17 @@ mod visit_statements;
mod visit_type;
use crate::{CallGraph, Pass, StructGraph, SymbolTable};
use crate::{CallGraph, Pass, StructGraph, SymbolTable, TypeTable};
use leo_ast::{Ast, Program};
use leo_errors::Result;
impl<'a> Pass for CodeGenerator<'a> {
type Input = (&'a Ast, &'a SymbolTable, &'a StructGraph, &'a CallGraph, &'a Program);
type Input = (&'a Ast, &'a SymbolTable, &'a TypeTable, &'a StructGraph, &'a CallGraph, &'a Program);
type Output = Result<String>;
fn do_pass((ast, symbol_table, struct_graph, call_graph, program): Self::Input) -> Self::Output {
let mut generator = Self::new(symbol_table, struct_graph, call_graph, program);
fn do_pass((ast, symbol_table, type_table, struct_graph, call_graph, program): Self::Input) -> Self::Output {
let mut generator = Self::new(symbol_table, type_table, struct_graph, call_graph, program);
let bytecode = generator.visit_program(ast.as_repr());
Ok(bytecode)

View File

@ -17,6 +17,7 @@
use crate::CodeGenerator;
use leo_ast::{
AccessExpression,
ArrayAccess,
ArrayExpression,
AssociatedConstant,
AssociatedFunction,
@ -165,7 +166,11 @@ impl<'a> CodeGenerator<'a> {
self.next_register += 1;
// Get the array type.
let array_type: String = todo!();
let array_type = match self.type_table.get(&input.id) {
Some(Type::Array(array_type)) => Type::Array(array_type),
_ => unreachable!("All types should be known at this phase of compilation"),
};
let array_type: String = self.visit_type(&array_type);
let array_instruction =
format!(" cast {expression_operands} into {destination_register} as {};\n", array_type);
@ -279,11 +284,22 @@ impl<'a> CodeGenerator<'a> {
(destination_register, instructions)
}
fn visit_member_access(&mut self, input: &'a MemberAccess) -> (String, String) {
let (inner_struct, _inner_instructions) = self.visit_expression(&input.inner);
let member_access_instruction = format!("{inner_struct}.{}", input.name);
fn visit_array_access(&mut self, input: &'a ArrayAccess) -> (String, String) {
let (array_operand, _) = self.visit_expression(&input.array);
let index_operand = match input.index.as_ref() {
Expression::Literal(Literal::Integer(_, string, _, _)) => format!("{}u32", string),
_ => unreachable!("Array indices must be integer literals"),
};
let array_access = format!("{}[{}]", array_operand, index_operand);
(member_access_instruction, String::new())
(array_access, String::new())
}
fn visit_member_access(&mut self, input: &'a MemberAccess) -> (String, String) {
let (inner_struct, _) = self.visit_expression(&input.inner);
let member_access = format!("{inner_struct}.{}", input.name);
(member_access, String::new())
}
// group::GEN -> group::GEN
@ -492,11 +508,13 @@ impl<'a> CodeGenerator<'a> {
fn visit_access(&mut self, input: &'a AccessExpression) -> (String, String) {
match input {
AccessExpression::Array(array) => todo!(),
AccessExpression::Array(array) => self.visit_array_access(array),
AccessExpression::Member(access) => self.visit_member_access(access),
AccessExpression::AssociatedConstant(constant) => self.visit_associated_constant(constant),
AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function),
AccessExpression::Tuple(_) => todo!(), // Tuples are not supported in AVM yet.
AccessExpression::Tuple(_) => {
unreachable!("Tuple access should not be in the AST at this phase of compilation.")
}
}
}

View File

@ -130,7 +130,8 @@ impl<'a> CodeGenerator<'a> {
// Construct and append the record variables.
for var in struct_.members.iter() {
writeln!(output_string, " {} as {};", var.identifier, var.type_,).expect("failed to write to string");
writeln!(output_string, " {} as {};", var.identifier, self.visit_type(&var.type_),)
.expect("failed to write to string");
}
output_string
@ -152,7 +153,8 @@ impl<'a> CodeGenerator<'a> {
writeln!(
output_string,
" {} as {}.{mode};", // todo: CAUTION private record variables only.
var.identifier, var.type_
var.identifier,
self.visit_type(&var.type_)
)
.expect("failed to write to string");
}
@ -291,9 +293,9 @@ impl<'a> CodeGenerator<'a> {
let (is_record, _) = self.composite_mapping.get(&identifier.name).unwrap();
match is_record {
// If the type is a record, then declare the type as is.
true => format!("{identifier}.record"),
// If the type is a struct, then add the public modifier.
false => format!("{identifier}.public"),
true => unreachable!("Type checking guarantees that mappings cannot contain records."),
}
}
type_ => format!("{type_}.public"),

View File

@ -19,7 +19,7 @@ use crate::CodeGenerator;
use leo_ast::{Mode, Type};
impl<'a> CodeGenerator<'a> {
pub(crate) fn visit_type(&mut self, input: &'a Type) -> String {
pub(crate) fn visit_type(&mut self, input: &Type) -> String {
match input {
Type::Address
| Type::Boolean
@ -30,7 +30,9 @@ impl<'a> CodeGenerator<'a> {
| Type::String
| Type::Identifier(..)
| Type::Integer(..) => format!("{input}"),
Type::Array(_) => todo!(),
Type::Array(array_type) => {
format!("[{}; {}u32]", self.visit_type(&array_type.element_type()), array_type.length())
}
Type::Mapping(_) => {
unreachable!("Mapping types are not supported at this phase of compilation")
}

View File

@ -30,6 +30,7 @@ use leo_ast::{
StructExpression,
StructVariableInitializer,
TernaryExpression,
TupleAccess,
Type,
};
@ -37,30 +38,18 @@ impl ExpressionReconstructor for Destructurer<'_> {
type AdditionalOutput = Vec<Statement>;
/// Replaces a tuple access expression with the appropriate expression.
fn reconstruct_access(&mut self, input: AccessExpression) -> (Expression, Self::AdditionalOutput) {
(
match input {
AccessExpression::Tuple(tuple_access) => {
// Lookup the expression in the tuple map.
match tuple_access.tuple.as_ref() {
Expression::Identifier(identifier) => {
match self
.tuples
.get(&identifier.name)
.and_then(|tuple| tuple.elements.get(tuple_access.index.value()))
{
Some(element) => element.clone(),
None => {
unreachable!("SSA guarantees that all tuples are declared and indices are valid.")
}
}
}
_ => unreachable!("SSA guarantees that subexpressions are identifiers or literals."),
fn reconstruct_tuple_access(&mut self, input: TupleAccess) -> (Expression, Self::AdditionalOutput) {
// Lookup the expression in the tuple map.
match input.tuple.as_ref() {
Expression::Identifier(identifier) => {
match self.tuples.get(&identifier.name).and_then(|tuple| tuple.elements.get(input.index.value())) {
Some(element) => (element.clone(), Default::default()),
None => {
unreachable!("SSA guarantees that all tuples are declared and indices are valid.")
}
}
_ => Expression::Access(input),
},
Default::default(),
)
}
_ => unreachable!("SSA guarantees that subexpressions are identifiers or literals."),
}
}
}

View File

@ -50,8 +50,6 @@ use leo_span::Symbol;
use indexmap::IndexMap;
pub struct Destructurer<'a> {
/// The symbol table associated with the program.
pub(crate) symbol_table: &'a SymbolTable,
/// A mapping between node IDs and their types.
pub(crate) type_table: &'a TypeTable,
/// A counter used to generate unique node IDs.
@ -63,25 +61,8 @@ pub struct Destructurer<'a> {
}
impl<'a> Destructurer<'a> {
pub(crate) fn new(
symbol_table: &'a SymbolTable,
type_table: &'a TypeTable,
node_builder: &'a NodeBuilder,
assigner: &'a Assigner,
) -> Self {
Self { symbol_table, type_table, node_builder, assigner, tuples: IndexMap::new() }
}
/// A wrapper around `assigner.unique_simple_assign_statement` that updates `self.structs`.
pub(crate) fn unique_simple_assign_statement(&mut self, expr: Expression) -> (Identifier, Statement) {
// Create a new variable for the expression.
let name = self.assigner.unique_symbol("$var", "$");
// Construct the lhs of the assignment.
let place = Identifier { name, span: Default::default(), id: self.node_builder.next_id() };
// Construct the assignment statement.
let statement = self.simple_assign_statement(place, expr);
(place, statement)
pub(crate) fn new(type_table: &'a TypeTable, node_builder: &'a NodeBuilder, assigner: &'a Assigner) -> Self {
Self { type_table, node_builder, assigner, tuples: IndexMap::new() }
}
/// A wrapper around `assigner.simple_assign_statement` that tracks the type of the lhs.

View File

@ -34,11 +34,11 @@ use leo_ast::{Ast, NodeBuilder, ProgramReconstructor};
use leo_errors::Result;
impl<'a> Pass for Destructurer<'a> {
type Input = (Ast, &'a SymbolTable, &'a TypeTable, &'a NodeBuilder, &'a Assigner);
type Input = (Ast, &'a TypeTable, &'a NodeBuilder, &'a Assigner);
type Output = Result<Ast>;
fn do_pass((ast, st, tt, node_builder, assigner): Self::Input) -> Self::Output {
let mut reconstructor = Destructurer::new(st, tt, node_builder, assigner);
fn do_pass((ast, tt, node_builder, assigner): Self::Input) -> Self::Output {
let mut reconstructor = Destructurer::new(tt, node_builder, assigner);
let program = reconstructor.reconstruct_program(ast.into_repr());
Ok(Ast::new(program))