1use crate::CompilerOptions;
22
23pub use leo_ast::Ast;
24use leo_ast::{NodeBuilder, Program, Stub};
25use leo_errors::{CompilerError, Result, emitter::Handler};
26use leo_passes::*;
27use leo_span::{Symbol, source_map::FileName, symbol::with_session_globals};
28
29use snarkvm::prelude::Network;
30
31use indexmap::{IndexMap, IndexSet};
32use std::{
33 fs,
34 path::{Path, PathBuf},
35};
36
37#[derive(Clone)]
39pub struct Compiler<'a, N: Network> {
40 handler: &'a Handler,
42 output_directory: PathBuf,
44 pub program_name: String,
46 pub ast: Ast,
48 compiler_options: CompilerOptions,
50 node_builder: NodeBuilder,
52 assigner: Assigner,
54 type_table: TypeTable,
56 import_stubs: IndexMap<Symbol, Stub>,
58 pub statements_before_dce: u32,
60 pub statements_after_dce: u32,
62 phantom: std::marker::PhantomData<N>,
64}
65
66impl<'a, N: Network> Compiler<'a, N> {
67 pub fn parse(&mut self, source: &str, filename: FileName) -> Result<()> {
68 let source_file = with_session_globals(|s| s.source_map.new_source(source, filename));
70
71 self.ast =
73 leo_parser::parse_ast::<N>(self.handler, &self.node_builder, &source_file.src, source_file.start_pos)?;
74
75 let program_scope = self.ast.ast.program_scopes.values().next().unwrap();
79 self.program_name = program_scope.program_id.name.to_string();
80
81 if self.compiler_options.output.initial_ast {
82 self.write_ast_to_json("initial_ast.json")?;
83 }
84
85 Ok(())
86 }
87
88 pub fn parse_from_file(&mut self, source_file_path: impl AsRef<Path>) -> Result<()> {
89 let source = fs::read_to_string(&source_file_path)
91 .map_err(|e| CompilerError::file_read_error(source_file_path.as_ref().display().to_string(), e))?;
92 self.parse(&source, FileName::Real(source_file_path.as_ref().into()))
93 }
94
95 pub fn new(
97 handler: &'a Handler,
98 output_directory: PathBuf,
99 compiler_options: Option<CompilerOptions>,
100 import_stubs: IndexMap<Symbol, Stub>,
101 ) -> Self {
102 let node_builder = NodeBuilder::default();
103 let assigner = Assigner::default();
104 let type_table = TypeTable::default();
105 Self {
106 handler,
107 output_directory,
108 program_name: Default::default(),
109 ast: Ast::new(Program::default()),
110 compiler_options: compiler_options.unwrap_or_default(),
111 node_builder,
112 assigner,
113 import_stubs,
114 statements_before_dce: 0,
115 statements_after_dce: 0,
116 type_table,
117 phantom: Default::default(),
118 }
119 }
120
121 pub fn symbol_table_pass(&self) -> Result<SymbolTable> {
123 let symbol_table = SymbolTableCreator::do_pass((&self.ast, self.handler))?;
124 Ok(symbol_table)
125 }
126
127 pub fn type_checker_pass(&'a self, symbol_table: &mut SymbolTable) -> Result<(StructGraph, CallGraph)> {
129 let (struct_graph, call_graph) =
130 TypeChecker::do_pass((&self.ast, self.handler, symbol_table, &self.type_table, NetworkLimits {
131 max_array_elements: N::MAX_ARRAY_ELEMENTS,
132 max_mappings: N::MAX_MAPPINGS,
133 max_functions: N::MAX_FUNCTIONS,
134 }))?;
135 Ok((struct_graph, call_graph))
136 }
137
138 pub fn static_analysis_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
140 StaticAnalyzer::<N>::do_pass((
141 &self.ast,
142 self.handler,
143 symbol_table,
144 &self.type_table,
145 self.compiler_options.build.conditional_block_max_depth,
146 self.compiler_options.build.disable_conditional_branch_type_checking,
147 ))
148 }
149
150 pub fn const_propagation_and_unroll_loop(&mut self, symbol_table: &mut SymbolTable) -> Result<()> {
152 const LARGE_LOOP_BOUND: usize = 1024usize;
153
154 for _ in 0..LARGE_LOOP_BOUND {
155 let loop_unroll_output = self.loop_unrolling_pass(symbol_table)?;
156
157 let const_prop_output = self.const_propagation_pass(symbol_table)?;
158
159 if !const_prop_output.changed && !loop_unroll_output.loop_unrolled {
160 if let Some(not_evaluated_span) = const_prop_output.const_not_evaluated {
162 return Err(CompilerError::const_not_evaluated(not_evaluated_span).into());
163 }
164
165 if let Some(not_evaluated_span) = const_prop_output.array_index_not_evaluated {
166 return Err(CompilerError::array_index_not_evaluated(not_evaluated_span).into());
167 }
168
169 if let Some(not_unrolled_span) = loop_unroll_output.loop_not_unrolled {
170 return Err(CompilerError::loop_bounds_not_evaluated(not_unrolled_span).into());
171 }
172
173 if self.compiler_options.output.unrolled_ast {
174 self.write_ast_to_json("unrolled_ast.json")?;
175 }
176
177 return Ok(());
178 }
179 }
180
181 Err(CompilerError::const_prop_unroll_many_loops(LARGE_LOOP_BOUND, Default::default()).into())
184 }
185
186 pub fn const_propagation_pass(&mut self, symbol_table: &mut SymbolTable) -> Result<ConstPropagatorOutput> {
188 let (ast, output) = ConstPropagator::do_pass((
189 std::mem::take(&mut self.ast),
190 self.handler,
191 symbol_table,
192 &self.type_table,
193 &self.node_builder,
194 ))?;
195
196 self.ast = ast;
197
198 Ok(output)
199 }
200
201 pub fn loop_unrolling_pass(&mut self, symbol_table: &mut SymbolTable) -> Result<UnrollerOutput> {
203 let (ast, output) = Unroller::do_pass((
204 std::mem::take(&mut self.ast),
205 self.handler,
206 &self.node_builder,
207 symbol_table,
208 &self.type_table,
209 ))?;
210
211 self.ast = ast;
212
213 Ok(output)
214 }
215
216 pub fn static_single_assignment_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
218 self.ast = StaticSingleAssigner::do_pass((
219 std::mem::take(&mut self.ast),
220 &self.node_builder,
221 &self.assigner,
222 symbol_table,
223 &self.type_table,
224 ))?;
225
226 if self.compiler_options.output.ssa_ast {
227 self.write_ast_to_json("ssa_ast.json")?;
228 }
229
230 Ok(())
231 }
232
233 pub fn flattening_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> {
235 self.ast = Flattener::do_pass((
236 std::mem::take(&mut self.ast),
237 symbol_table,
238 &self.type_table,
239 &self.node_builder,
240 &self.assigner,
241 ))?;
242
243 if self.compiler_options.output.flattened_ast {
244 self.write_ast_to_json("flattened_ast.json")?;
245 }
246
247 Ok(())
248 }
249
250 pub fn destructuring_pass(&mut self) -> Result<()> {
252 self.ast = Destructurer::do_pass((
253 std::mem::take(&mut self.ast),
254 &self.type_table,
255 &self.node_builder,
256 &self.assigner,
257 ))?;
258
259 if self.compiler_options.output.destructured_ast {
260 self.write_ast_to_json("destructured_ast.json")?;
261 }
262
263 Ok(())
264 }
265
266 pub fn function_inlining_pass(&mut self, call_graph: &CallGraph) -> Result<()> {
268 let ast = FunctionInliner::do_pass((
269 std::mem::take(&mut self.ast),
270 &self.node_builder,
271 call_graph,
272 &self.type_table,
273 ))?;
274 self.ast = ast;
275
276 if self.compiler_options.output.inlined_ast {
277 self.write_ast_to_json("inlined_ast.json")?;
278 }
279
280 Ok(())
281 }
282
283 pub fn dead_code_elimination_pass(&mut self) -> Result<()> {
285 if self.compiler_options.build.dce_enabled {
286 let output = DeadCodeEliminator::do_pass((std::mem::take(&mut self.ast), &self.type_table))?;
287 self.ast = output.ast;
288 self.statements_before_dce = output.statements_before;
289 self.statements_after_dce = output.statements_after;
290 }
291
292 if self.compiler_options.output.dce_ast {
293 self.write_ast_to_json("dce_ast.json")?;
294 }
295
296 Ok(())
297 }
298
299 pub fn code_generation_pass(
301 &mut self,
302 symbol_table: &SymbolTable,
303 struct_graph: &StructGraph,
304 call_graph: &CallGraph,
305 ) -> Result<String> {
306 CodeGenerator::do_pass((&self.ast, symbol_table, &self.type_table, struct_graph, call_graph, &self.ast.ast))
307 }
308
309 pub fn intermediate_passes(&mut self) -> Result<(SymbolTable, StructGraph, CallGraph)> {
311 let mut st = self.symbol_table_pass()?;
312
313 let (struct_graph, call_graph) = self.type_checker_pass(&mut st)?;
314
315 self.static_analysis_pass(&st)?;
316
317 self.const_propagation_and_unroll_loop(&mut st)?;
318
319 self.static_single_assignment_pass(&st)?;
320
321 self.flattening_pass(&st)?;
322
323 self.destructuring_pass()?;
324
325 self.function_inlining_pass(&call_graph)?;
326
327 self.dead_code_elimination_pass()?;
328
329 Ok((st, struct_graph, call_graph))
330 }
331
332 pub fn compile(&mut self, source: &str, filename: FileName) -> Result<String> {
334 self.parse(source, filename)?;
336 self.add_import_stubs()?;
338 let (symbol_table, struct_graph, call_graph) = self.intermediate_passes()?;
340 let bytecode = self.code_generation_pass(&symbol_table, &struct_graph, &call_graph)?;
342 Ok(bytecode)
343 }
344
345 pub fn compile_from_file(&mut self, source_file_path: impl AsRef<Path>) -> Result<String> {
346 let source = fs::read_to_string(&source_file_path)
347 .map_err(|e| CompilerError::file_read_error(source_file_path.as_ref().display().to_string(), e))?;
348 self.compile(&source, FileName::Real(source_file_path.as_ref().into()))
349 }
350
351 fn write_ast_to_json(&self, file_suffix: &str) -> Result<()> {
353 if self.compiler_options.output.ast_spans_enabled {
355 self.ast.to_json_file(self.output_directory.clone(), &format!("{}.{file_suffix}", self.program_name))?;
356 } else {
357 self.ast.to_json_file_without_keys(
358 self.output_directory.clone(),
359 &format!("{}.{file_suffix}", self.program_name),
360 &["_span", "span"],
361 )?;
362 }
363 Ok(())
364 }
365
366 pub fn add_import_stubs(&mut self) -> Result<()> {
368 let (mut unexplored, mut explored): (IndexSet<Symbol>, IndexSet<Symbol>) =
370 (self.ast.ast.imports.keys().cloned().collect(), IndexSet::new());
371 while !unexplored.is_empty() {
372 let mut current_dependencies: IndexSet<Symbol> = IndexSet::new();
373 for program_name in unexplored.iter() {
374 if let Some(stub) = self.import_stubs.get(program_name) {
375 explored.insert(*program_name);
377 for dependency in stub.imports.iter() {
378 if explored.insert(dependency.name.name) {
380 current_dependencies.insert(dependency.name.name);
381 }
382 }
383 } else {
384 return Err(CompilerError::imported_program_not_found(
385 self.program_name.clone(),
386 *program_name,
387 self.ast.ast.imports[program_name].1,
388 )
389 .into());
390 }
391 }
392
393 unexplored = current_dependencies;
395 }
396
397 self.ast.ast.stubs =
399 self.import_stubs.clone().into_iter().filter(|(program_name, _)| explored.contains(program_name)).collect();
400 Ok(())
401 }
402}