cairo_lang_compiler/
lib.rs1use std::path::Path;
6use std::sync::{Arc, Mutex};
7
8use ::cairo_lang_diagnostics::ToOption;
9use anyhow::{Context, Result};
10use cairo_lang_filesystem::ids::CrateId;
11use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
12use cairo_lang_lowering::utils::InliningStrategy;
13use cairo_lang_sierra::debug_info::{Annotations, DebugInfo};
14use cairo_lang_sierra::program::{Program, ProgramArtifact};
15use cairo_lang_sierra_generator::db::SierraGenGroup;
16use cairo_lang_sierra_generator::executables::{collect_executables, find_executable_function_ids};
17use cairo_lang_sierra_generator::program_generator::{
18 SierraProgramWithDebug, try_get_function_with_body_id,
19};
20use cairo_lang_sierra_generator::replace_ids::replace_sierra_ids_in_program;
21use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
22
23use crate::db::RootDatabase;
24use crate::diagnostics::DiagnosticsReporter;
25use crate::project::{ProjectConfig, get_main_crate_ids_from_project, setup_project};
26
27pub mod db;
28pub mod diagnostics;
29pub mod project;
30
31#[cfg(test)]
32mod test;
33
34#[derive(Default)]
36pub struct CompilerConfig<'c> {
37 pub diagnostics_reporter: DiagnosticsReporter<'c>,
38
39 pub replace_ids: bool,
41
42 pub inlining_strategy: InliningStrategy,
44
45 pub allowed_libfuncs_list_name: Option<String>,
48
49 pub add_statements_functions: bool,
52
53 pub add_statements_code_locations: bool,
56}
57
58pub fn compile_cairo_project_at_path(
69 path: &Path,
70 compiler_config: CompilerConfig<'_>,
71) -> Result<Program> {
72 let mut db = RootDatabase::builder()
73 .with_inlining_strategy(compiler_config.inlining_strategy)
74 .detect_corelib()
75 .build()?;
76 let main_crate_ids = setup_project(&mut db, path)?;
77 compile_prepared_db_program(&mut db, main_crate_ids, compiler_config)
78}
79
80pub fn compile(
90 project_config: ProjectConfig,
91 compiler_config: CompilerConfig<'_>,
92) -> Result<Program> {
93 let mut db = RootDatabase::builder().with_project_config(project_config.clone()).build()?;
94 let main_crate_ids = get_main_crate_ids_from_project(&mut db, &project_config);
95
96 compile_prepared_db_program(&mut db, main_crate_ids, compiler_config)
97}
98
99pub fn compile_prepared_db_program(
111 db: &mut RootDatabase,
112 main_crate_ids: Vec<CrateId>,
113 compiler_config: CompilerConfig<'_>,
114) -> Result<Program> {
115 Ok(compile_prepared_db(db, main_crate_ids, compiler_config)?.program)
116}
117
118pub fn compile_prepared_db(
133 db: &RootDatabase,
134 main_crate_ids: Vec<CrateId>,
135 mut compiler_config: CompilerConfig<'_>,
136) -> Result<SierraProgramWithDebug> {
137 compiler_config.diagnostics_reporter.ensure(db)?;
138
139 let mut sierra_program_with_debug = Arc::unwrap_or_clone(
140 db.get_sierra_program(main_crate_ids)
141 .to_option()
142 .context("Compilation failed without any diagnostics")?,
143 );
144
145 if compiler_config.replace_ids {
146 sierra_program_with_debug.program =
147 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
148 }
149
150 Ok(sierra_program_with_debug)
151}
152
153fn warmup_db_blocking(
158 snapshot: salsa::Snapshot<RootDatabase>,
159 requested_function_ids: Vec<ConcreteFunctionWithBodyId>,
160) {
161 let processed_function_ids =
162 &Mutex::new(UnorderedHashSet::<ConcreteFunctionWithBodyId>::default());
163 rayon::scope(move |s| {
164 for func_id in requested_function_ids {
165 let snapshot = salsa::ParallelDatabase::snapshot(&*snapshot);
166
167 s.spawn(move |_| {
168 fn handle_func_inner(
169 processed_function_ids: &Mutex<UnorderedHashSet<ConcreteFunctionWithBodyId>>,
170 snapshot: salsa::Snapshot<RootDatabase>,
171 func_id: ConcreteFunctionWithBodyId,
172 ) {
173 if processed_function_ids.lock().unwrap().insert(func_id) {
174 rayon::scope(move |s| {
175 let db = &*snapshot;
176 let Ok(function) = db.function_with_body_sierra(func_id) else {
177 return;
178 };
179 for statement in &function.body {
180 let Some(related_function_id) =
181 try_get_function_with_body_id(db, statement)
182 else {
183 continue;
184 };
185
186 let snapshot = salsa::ParallelDatabase::snapshot(&*snapshot);
187 s.spawn(move |_| {
188 handle_func_inner(
189 processed_function_ids,
190 snapshot,
191 related_function_id,
192 )
193 })
194 }
195 });
196 }
197 }
198
199 handle_func_inner(processed_function_ids, snapshot, func_id)
200 });
201 }
202 });
203}
204
205fn spawn_warmup_db(db: &RootDatabase, requested_function_ids: Vec<ConcreteFunctionWithBodyId>) {
207 let snapshot = salsa::ParallelDatabase::snapshot(db);
208 rayon::spawn(move || warmup_db_blocking(snapshot, requested_function_ids));
209}
210
211pub fn get_sierra_program_for_functions(
214 db: &RootDatabase,
215 requested_function_ids: Vec<ConcreteFunctionWithBodyId>,
216 mut diagnostic_reporter: DiagnosticsReporter<'_>,
217) -> Result<Arc<SierraProgramWithDebug>> {
218 if rayon::current_num_threads() > 1 {
219 diagnostic_reporter.warm_up_diagnostics(db);
221 spawn_warmup_db(db, requested_function_ids.clone());
222 }
223
224 diagnostic_reporter.ensure(db)?;
225 db.get_sierra_program_for_functions(requested_function_ids)
226 .to_option()
227 .with_context(|| "Compilation failed without any diagnostics.")
228}
229
230pub fn compile_prepared_db_program_artifact(
245 db: &mut RootDatabase,
246 main_crate_ids: Vec<CrateId>,
247 mut compiler_config: CompilerConfig<'_>,
248) -> Result<ProgramArtifact> {
249 let add_statements_functions = compiler_config.add_statements_functions;
250 let add_statements_code_locations = compiler_config.add_statements_code_locations;
251
252 compiler_config.diagnostics_reporter.ensure(db)?;
253
254 let executable_functions = find_executable_function_ids(db, main_crate_ids.clone());
255
256 let mut sierra_program_with_debug = if executable_functions.is_empty() {
257 Arc::unwrap_or_clone(
260 db.get_sierra_program(main_crate_ids)
261 .to_option()
262 .context("Compilation failed without any diagnostics")?,
263 )
264 } else {
265 Arc::unwrap_or_clone(
267 db.get_sierra_program_for_functions(executable_functions.clone().into_keys().collect())
268 .to_option()
269 .context("Compilation failed without any diagnostics")?,
270 )
271 };
272
273 if compiler_config.replace_ids {
274 sierra_program_with_debug.program =
275 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
276 }
277
278 let mut annotations = Annotations::default();
279
280 if add_statements_functions {
281 annotations.extend(Annotations::from(
282 sierra_program_with_debug
283 .debug_info
284 .statements_locations
285 .extract_statements_functions(db),
286 ))
287 };
288
289 if add_statements_code_locations {
290 annotations.extend(Annotations::from(
291 sierra_program_with_debug
292 .debug_info
293 .statements_locations
294 .extract_statements_source_code_locations(db),
295 ))
296 };
297
298 let debug_info = DebugInfo {
299 type_names: Default::default(),
300 libfunc_names: Default::default(),
301 user_func_names: Default::default(),
302 annotations,
303 executables: Default::default(),
304 };
305
306 let executables =
308 collect_executables(db, executable_functions, &sierra_program_with_debug.program);
309
310 Ok(ProgramArtifact::stripped(sierra_program_with_debug.program)
311 .with_debug_info(DebugInfo { executables, ..debug_info }))
312}