mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-27 12:17:35 +03:00
Support codegen for arrays
This commit is contained in:
parent
c80aee091a
commit
49a0c7a469
@ -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)?;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"),
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user