Add Destructuring pass to the compiler

This commit is contained in:
Pranav Gaddamadugu 2023-10-13 11:34:36 -04:00 committed by Pranav Gaddamadugu
parent b1096f1036
commit a3c0892ffd
13 changed files with 90 additions and 18 deletions

View File

@ -242,6 +242,23 @@ impl<'a> Compiler<'a> {
Ok(())
}
/// Runs the destructuring pass.
pub fn destructuring_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
self.ast = Destructurer::do_pass((
std::mem::take(&mut self.ast),
symbol_table,
&self.type_table,
&self.node_builder,
&self.assigner,
))?;
if self.compiler_options.output.destructured_ast {
self.write_ast_to_json("destructured_ast.json")?;
}
Ok(())
}
/// Runs the function inlining pass.
pub fn function_inlining_pass(&mut self, call_graph: &CallGraph) -> Result<()> {
let ast = FunctionInliner::do_pass((
@ -295,6 +312,8 @@ impl<'a> Compiler<'a> {
self.flattening_pass(&st)?;
self.destructuring_pass(&st)?;
self.function_inlining_pass(&call_graph)?;
self.dead_code_elimination_pass()?;

View File

@ -52,6 +52,8 @@ pub struct OutputOptions {
pub ssa_ast: bool,
/// If enabled writes the AST after flattening.
pub flattened_ast: bool,
/// If enabled writes the AST after destructuring.
pub destructured_ast: bool,
/// If enabled writes the AST after inlining.
pub inlined_ast: bool,
/// If enabled writes the AST after dead code elimination.

View File

@ -66,6 +66,7 @@ struct CompileOutput {
pub unrolled_ast: String,
pub ssa_ast: String,
pub flattened_ast: String,
pub destructured_ast: String,
pub inlined_ast: String,
pub dce_ast: String,
pub bytecode: String,
@ -95,6 +96,7 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,
unrolled_ast: true,
ssa_ast: true,
flattened_ast: true,
destructured_ast: true,
inlined_ast: true,
dce_ast: true,
},
@ -116,7 +118,7 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,
handler.extend_if_error(package.get_process().map_err(LeoError::Anyhow))?;
// Hash the ast files.
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) = hash_asts();
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, destructured_ast, inlined_ast, dce_ast) = hash_asts();
// Hash the symbol tables.
let (initial_symbol_table, type_checked_symbol_table, unrolled_symbol_table) = hash_symbol_tables();
@ -134,6 +136,7 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,
unrolled_ast,
ssa_ast,
flattened_ast,
destructured_ast,
inlined_ast,
dce_ast,
bytecode: hash_content(&bytecode),

View File

@ -74,6 +74,7 @@ struct ExecuteOutput {
pub unrolled_ast: String,
pub ssa_ast: String,
pub flattened_ast: String,
pub destructured_ast: String,
pub inlined_ast: String,
pub dce_ast: String,
pub bytecode: String,
@ -109,6 +110,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
unrolled_ast: true,
ssa_ast: true,
flattened_ast: true,
destructured_ast: true,
inlined_ast: true,
dce_ast: true,
},
@ -200,7 +202,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
}
// Hash the ast files.
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) = hash_asts();
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, destructured_ast, inlined_ast, dce_ast) = hash_asts();
// Hash the symbol tables.
let (initial_symbol_table, type_checked_symbol_table, unrolled_symbol_table) = hash_symbol_tables();
@ -218,6 +220,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
unrolled_ast,
ssa_ast,
flattened_ast,
destructured_ast,
inlined_ast,
dce_ast,
bytecode: hash_content(&bytecode),

View File

@ -45,15 +45,16 @@ pub type Network = Testnet3;
#[allow(unused)]
pub type Aleo = snarkvm::circuit::AleoV0;
pub fn hash_asts() -> (String, String, String, String, String, String) {
pub fn hash_asts() -> (String, String, String, String, String, String, String) {
let initial_ast = hash_file("/tmp/output/test.initial_ast.json");
let unrolled_ast = hash_file("/tmp/output/test.unrolled_ast.json");
let ssa_ast = hash_file("/tmp/output/test.ssa_ast.json");
let flattened_ast = hash_file("/tmp/output/test.flattened_ast.json");
let destructured_ast = hash_file("/tmp/output/test.destructured_ast.json");
let inlined_ast = hash_file("/tmp/output/test.inlined_ast.json");
let dce_ast = hash_file("/tmp/output/test.dce_ast.json");
(initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast)
(initial_ast, unrolled_ast, ssa_ast, flattened_ast, destructured_ast, inlined_ast, dce_ast)
}
pub fn hash_symbol_tables() -> (String, String, String) {
@ -236,6 +237,8 @@ pub fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<String, L
parsed.flattening_pass(&st)?;
parsed.destructuring_pass(&st)?;
parsed.function_inlining_pass(&call_graph)?;
parsed.dead_code_elimination_pass()?;

View File

@ -34,8 +34,6 @@ use leo_ast::{
Type,
};
// TODO: Clean up logic. To be done in a follow-up PR (feat/tuples)
impl ExpressionReconstructor for Flattener<'_> {
type AdditionalOutput = Vec<Statement>;

View File

@ -26,6 +26,9 @@ pub use common::*;
pub mod dead_code_elimination;
pub use dead_code_elimination::*;
pub mod destructuring;
pub use destructuring::*;
pub mod flattening;
pub use flattening::*;

View File

@ -1060,28 +1060,28 @@ impl<'a> TypeChecker<'a> {
/// Emits an error if the type or its constituent types is not valid.
pub(crate) fn assert_type_is_valid(&self, type_: &Type, span: Span) -> bool {
let mut is_defined = true;
let mut is_valid = true;
match type_ {
// String types are temporarily disabled.
Type::String => {
is_defined = false;
is_valid = false;
self.emit_err(TypeCheckerError::strings_are_not_supported(span));
}
// Check that the named composite type has been defined.
Type::Identifier(identifier) if self.symbol_table.borrow().lookup_struct(identifier.name).is_none() => {
is_defined = false;
is_valid = false;
self.emit_err(TypeCheckerError::undefined_type(identifier.name, span));
}
// Check that the constituent types of the tuple are valid.
Type::Tuple(tuple_type) => {
for type_ in tuple_type.elements().iter() {
is_defined &= self.assert_type_is_valid(type_, span)
is_valid &= self.assert_type_is_valid(type_, span)
}
}
// Check that the constituent types of mapping are valid.
Type::Mapping(mapping_type) => {
is_defined &= self.assert_type_is_valid(&mapping_type.key, span);
is_defined &= self.assert_type_is_valid(&mapping_type.value, span);
is_valid &= self.assert_type_is_valid(&mapping_type.key, span);
is_valid &= self.assert_type_is_valid(&mapping_type.value, span);
}
// Check that the array element types are valid.
Type::Array(array_type) => {
@ -1093,11 +1093,15 @@ impl<'a> TypeChecker<'a> {
self.emit_err(TypeCheckerError::array_too_large(length, Testnet3::MAX_ARRAY_ELEMENTS, span))
}
}
is_defined &= self.assert_type_is_valid(array_type.element_type(), span)
// Check that the array element type is not a tuple.
if let Type::Tuple(_) = array_type.element_type() {
self.emit_err(TypeCheckerError::array_element_cannot_be_tuple(span))
}
is_valid &= self.assert_type_is_valid(array_type.element_type(), span)
}
_ => {} // Do nothing.
}
is_defined
is_valid
}
/// Emits an error if the type is not a mapping.

View File

@ -698,4 +698,11 @@ create_messages!(
msg: format!("An array cannot have more than {max} elements, found one with {size} elements"),
help: None,
}
@formatted
array_element_cannot_be_tuple {
args: (),
msg: format!("An array cannot have a tuple as an element type"),
help: None,
}
);

View File

@ -53,6 +53,7 @@ impl From<BuildOptions> for CompilerOptions {
unrolled_ast: options.enable_unrolled_ast_snapshot,
ssa_ast: options.enable_ssa_ast_snapshot,
flattened_ast: options.enable_flattened_ast_snapshot,
destructured_ast: options.enable_destructured_ast_snapshot,
inlined_ast: options.enable_inlined_ast_snapshot,
dce_ast: options.enable_dce_ast_snapshot,
},
@ -63,6 +64,7 @@ impl From<BuildOptions> for CompilerOptions {
out_options.output.unrolled_ast = true;
out_options.output.ssa_ast = true;
out_options.output.flattened_ast = true;
out_options.output.destructured_ast = true;
out_options.output.inlined_ast = true;
out_options.output.dce_ast = true;
}

View File

@ -145,6 +145,8 @@ pub struct BuildOptions {
pub enable_ssa_ast_snapshot: bool,
#[clap(long, help = "Writes AST snapshot of the flattened AST.")]
pub enable_flattened_ast_snapshot: bool,
#[clap(long, help = "Writes AST snapshot of the destructured AST.")]
pub enable_destructured_ast_snapshot: bool,
#[clap(long, help = "Writes AST snapshot of the inlined AST.")]
pub enable_inlined_ast_snapshot: bool,
#[clap(long, help = "Writes AST snapshot of the dead code eliminated (DCE) AST.")]

View File

@ -7,9 +7,9 @@ outputs:
unrolled_symbol_table: 583ed0adba552a2abfd5927ecebd5c20fa943380c8d0b45e00a33e7c03de3300
initial_ast: 34a1afc5b8e850d3ca1fffbabcfb5ea6bacea945b20628868784f8a5b4140854
unrolled_ast: 34a1afc5b8e850d3ca1fffbabcfb5ea6bacea945b20628868784f8a5b4140854
ssa_ast: df3ca19301a9f38667d034bfdb7ae892292ffa822eaa491e72cf75730c6355be
flattened_ast: ede7aeb18282c9c9b48b3b60212fea5fb8886321d4a0742e96b2b640129b0463
inlined_ast: ede7aeb18282c9c9b48b3b60212fea5fb8886321d4a0742e96b2b640129b0463
dce_ast: a53f58149602de59ef43a6987aac4e8fbec357995ca89e3d5093aedce9af73f4
ssa_ast: bb6b1c14a80f6c148484d7e330bac0d6591163a2a98d0787f530ae5d28dcaed3
flattened_ast: 4752b34e62f67e177c91688f03fe3f1f827420b09e586d5daccfbe19beb5ab18
inlined_ast: 4752b34e62f67e177c91688f03fe3f1f827420b09e586d5daccfbe19beb5ab18
dce_ast: 4d25901f577f93b7a8ab66c82774d538f648eb0d7675c3d4513e558f8a19c967
bytecode: 4f4c5c377fed78feede8ee754c9f838f449f8d00cf771b2bb65884e876f90b7e
warnings: ""

View File

@ -41,6 +41,8 @@ enum BenchMode {
Ssa,
/// Benchmarks flattening.
Flatten,
/// Benchmarks destructuring.
Destructure,
/// Benchmarks function inlining.
Inline,
/// Benchmarks dead code elimination.
@ -98,6 +100,7 @@ fn new_compiler(handler: &Handler) -> Compiler<'_> {
unrolled_ast: false,
ssa_ast: false,
flattened_ast: false,
destructured_ast: false,
inlined_ast: false,
dce_ast: false,
},
@ -125,6 +128,7 @@ impl Sample {
BenchMode::Unroll => self.bench_loop_unroller(c),
BenchMode::Ssa => self.bench_ssa(c),
BenchMode::Flatten => self.bench_flattener(c),
BenchMode::Destructure => self.bench_destructurer(c),
BenchMode::Inline => self.bench_inline(c),
BenchMode::Dce => self.bench_dce(c),
BenchMode::Codegen => self.bench_codegen(c),
@ -229,6 +233,22 @@ impl Sample {
});
}
fn bench_destructurer(&self, c: &mut Criterion) {
self.bencher_after_parse(c, "destructurer pass", |mut compiler| {
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
let (symbol_table, _struct_graph, _call_graph) =
compiler.type_checker_pass(symbol_table).expect("failed to run type check pass");
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass");
let start = Instant::now();
let out = compiler.destructuring_pass(&symbol_table);
let time = start.elapsed();
out.expect("failed to run destructurer pass");
time
});
}
fn bench_inline(&self, c: &mut Criterion) {
self.bencher_after_parse(c, "inliner pass", |mut compiler| {
let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table");
@ -237,6 +257,7 @@ impl Sample {
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass");
compiler.destructuring_pass(&symbol_table).expect("failed to run destructurer pass");
let start = Instant::now();
let out = compiler.function_inlining_pass(&call_graph);
let time = start.elapsed();
@ -253,6 +274,7 @@ impl Sample {
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass");
compiler.destructuring_pass(&symbol_table).expect("failed to run destructurer pass");
compiler.function_inlining_pass(&call_graph).expect("failed to run inliner pass");
let start = Instant::now();
let out = compiler.dead_code_elimination_pass();
@ -270,6 +292,7 @@ impl Sample {
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass");
compiler.destructuring_pass(&symbol_table).expect("failed to run destructurer pass");
compiler.function_inlining_pass(&call_graph).expect("failed to run inliner pass");
compiler.dead_code_elimination_pass().expect("failed to run dce pass");
let start = Instant::now();
@ -291,6 +314,7 @@ impl Sample {
let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass");
compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass");
compiler.flattening_pass(&symbol_table).expect("failed to run flattening pass");
compiler.destructuring_pass(&symbol_table).expect("failed to run destructuring pass");
compiler.function_inlining_pass(&call_graph).expect("failed to run function inlining pass");
compiler.dead_code_elimination_pass().expect("failed to run dce pass");
compiler
@ -315,6 +339,7 @@ bench!(bench_type, BenchMode::Type);
bench!(bench_unroll, BenchMode::Unroll);
bench!(bench_ssa, BenchMode::Ssa);
bench!(bench_flatten, BenchMode::Flatten);
bench!(bench_destructure, BenchMode::Destructure);
bench!(bench_inline, BenchMode::Inline);
bench!(bench_dce, BenchMode::Dce);
bench!(bench_codegen, BenchMode::Codegen);
@ -330,6 +355,7 @@ criterion_group!(
bench_unroll,
bench_ssa,
bench_flatten,
bench_destructure,
bench_inline,
bench_dce,
bench_codegen,