1use std::path::Path;
6use std::sync::Mutex;
7
8use ::cairo_lang_diagnostics::ToOption;
9use anyhow::{Context, Result};
10use cairo_lang_defs::db::DefsGroup;
11use cairo_lang_filesystem::ids::{CrateId, CrateInput};
12use cairo_lang_lowering::db::LoweringGroup;
13use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
14use cairo_lang_lowering::optimizations::config::Optimizations;
15use cairo_lang_lowering::utils::InliningStrategy;
16use cairo_lang_parser::db::ParserGroup;
17use cairo_lang_semantic::db::SemanticGroup;
18use cairo_lang_sierra::debug_info::{Annotations, DebugInfo};
19use cairo_lang_sierra::program::{Program, ProgramArtifact};
20use cairo_lang_sierra_generator::db::SierraGenGroup;
21use cairo_lang_sierra_generator::executables::{collect_executables, find_executable_function_ids};
22use cairo_lang_sierra_generator::program_generator::{
23 SierraProgramWithDebug, find_all_free_function_ids, try_get_function_with_body_id,
24};
25use cairo_lang_sierra_generator::replace_ids::replace_sierra_ids_in_program;
26use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
27use cairo_lang_utils::{CloneableDatabase, Intern};
28use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
29use salsa::Database;
30
31use crate::db::RootDatabase;
32use crate::diagnostics::{DiagnosticsError, DiagnosticsReporter};
33use crate::project::{ProjectConfig, get_main_crate_ids_from_project, setup_project};
34
35pub mod db;
36pub mod diagnostics;
37pub mod project;
38
39#[cfg(test)]
40mod test;
41
42#[derive(Default)]
44pub struct CompilerConfig<'a> {
45 pub diagnostics_reporter: DiagnosticsReporter<'a>,
46
47 pub replace_ids: bool,
49
50 pub add_statements_functions: bool,
53
54 pub add_statements_code_locations: bool,
57
58 pub add_functions_debug_info: bool,
61}
62
63pub fn compile_cairo_project_at_path(
74 path: &Path,
75 compiler_config: CompilerConfig<'_>,
76 inlining_strategy: InliningStrategy,
77) -> Result<Program> {
78 let mut db = RootDatabase::builder()
79 .with_optimizations(Optimizations::enabled_with_default_movable_functions(
80 inlining_strategy,
81 ))
82 .detect_corelib()
83 .build()?;
84 let main_crate_ids = setup_project(&mut db, path)?;
85 compile_prepared_db_program(
86 &db,
87 CrateInput::into_crate_ids(&db, main_crate_ids),
88 compiler_config,
89 )
90}
91
92pub fn compile(
102 project_config: ProjectConfig,
103 compiler_config: CompilerConfig<'_>,
104) -> Result<Program> {
105 let db = RootDatabase::builder()
106 .with_optimizations(Optimizations::enabled_with_default_movable_functions(
107 InliningStrategy::Default,
108 ))
109 .with_project_config(project_config.clone())
110 .build()?;
111 let main_crate_ids = get_main_crate_ids_from_project(&db, &project_config);
112
113 compile_prepared_db_program(&db, main_crate_ids, compiler_config)
114}
115
116pub fn compile_prepared_db_program<'db>(
128 db: &'db dyn Database,
129 main_crate_ids: Vec<CrateId<'db>>,
130 compiler_config: CompilerConfig<'_>,
131) -> Result<Program> {
132 Ok(compile_prepared_db(db, main_crate_ids, compiler_config)?.program)
133}
134
135pub fn compile_prepared_db<'db>(
150 db: &'db dyn Database,
151 main_crate_ids: Vec<CrateId<'db>>,
152 mut compiler_config: CompilerConfig<'_>,
153) -> Result<SierraProgramWithDebug<'db>> {
154 compiler_config.diagnostics_reporter.ensure(db)?;
155
156 let mut sierra_program_with_debug = db
157 .get_sierra_program(main_crate_ids)
158 .to_option()
159 .context("Compilation failed without any diagnostics")?
160 .clone();
161
162 if compiler_config.replace_ids {
163 sierra_program_with_debug.program =
164 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
165 }
166
167 Ok(sierra_program_with_debug)
168}
169
170fn should_warmup() -> bool {
172 rayon::current_num_threads() > 1
173}
174
175pub fn ensure_diagnostics(
186 db: &dyn CloneableDatabase,
187 diagnostic_reporter: &mut DiagnosticsReporter<'_>,
188) -> std::result::Result<(), DiagnosticsError> {
189 if should_warmup() {
190 let crates = diagnostic_reporter.crates_of_interest(db);
191 let warmup_db = db.dyn_clone();
192 let ensure_db = db.dyn_clone();
193 rayon::join(
194 move || warmup_diagnostics_blocking(warmup_db.as_ref(), crates),
195 move || diagnostic_reporter.ensure(ensure_db.as_ref()),
196 )
197 .1
198 } else {
199 diagnostic_reporter.ensure(db)
200 }
201}
202
203fn warmup_diagnostics_blocking(db: &dyn CloneableDatabase, crates: Vec<CrateInput>) {
206 crates.into_par_iter().for_each_with(db.dyn_clone(), |db, crate_input| {
207 let db = db.as_ref();
208 let crate_id = crate_input.into_crate_long_id(db).intern(db);
209 db.crate_modules(crate_id).into_par_iter().for_each_with(
210 db.dyn_clone(),
211 |db, module_id| {
212 for file_id in db.module_files(*module_id).unwrap_or_default().iter().copied() {
213 db.file_syntax_diagnostics(file_id);
214 }
215 let _ = db.module_semantic_diagnostics(*module_id);
216 let _ = db.module_lowering_diagnostics(*module_id);
217 },
218 );
219 });
220}
221
222fn warmup_functions_blocking<'db>(
227 db: &dyn CloneableDatabase,
228 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
229) {
230 let processed_function_ids = &Mutex::new(UnorderedHashSet::<salsa::Id>::default());
231 requested_function_ids.into_par_iter().for_each_with(db.dyn_clone(), move |db, func_id| {
232 fn handle_func_inner<'db>(
233 processed_function_ids: &Mutex<UnorderedHashSet<salsa::Id>>,
234 db: &dyn CloneableDatabase,
235 func_id: ConcreteFunctionWithBodyId<'db>,
236 ) {
237 if processed_function_ids.lock().unwrap().insert(func_id.as_intern_id()) {
238 let Ok(function) = db.function_with_body_sierra(func_id) else {
239 return;
240 };
241 function.body.par_iter().for_each_with(db.dyn_clone(), move |db, statement| {
242 let related_function_id: ConcreteFunctionWithBodyId<'_> =
243 if let Some(r_id) = try_get_function_with_body_id(db.as_ref(), statement) {
244 r_id
245 } else {
246 return;
247 };
248
249 handle_func_inner(processed_function_ids, db.as_ref(), related_function_id);
250 });
251 }
252 }
253 handle_func_inner(processed_function_ids, db.as_ref(), func_id)
254 });
255}
256
257pub fn get_sierra_program_for_functions<'db>(
260 db: &'db dyn CloneableDatabase,
261 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
262) -> Result<&'db SierraProgramWithDebug<'db>> {
263 if should_warmup() {
264 let requested_function_ids = requested_function_ids.clone();
265 warmup_functions_blocking(db, requested_function_ids);
266 }
267 db.get_sierra_program_for_functions(requested_function_ids)
268 .to_option()
269 .context("Compilation failed without any diagnostics.")
270}
271
272pub fn compile_prepared_db_program_artifact<'db>(
287 db: &'db dyn CloneableDatabase,
288 main_crate_ids: Vec<CrateId<'db>>,
289 mut compiler_config: CompilerConfig<'_>,
290) -> Result<ProgramArtifact> {
291 ensure_diagnostics(db, &mut compiler_config.diagnostics_reporter)?;
292
293 let executable_functions = find_executable_function_ids(db, main_crate_ids.clone());
294
295 let function_ids = if executable_functions.is_empty() {
296 find_all_free_function_ids(db, main_crate_ids)
299 .to_option()
300 .context("Compilation failed without any diagnostics.")?
301 } else {
302 executable_functions.keys().cloned().collect()
304 };
305
306 let mut program_artifact =
307 compile_prepared_db_program_artifact_for_functions(db, function_ids, compiler_config)?;
308
309 let executables = collect_executables(db, executable_functions, &program_artifact.program);
311
312 let debug_info = program_artifact.debug_info.take().unwrap_or_default();
313
314 Ok(program_artifact.with_debug_info(DebugInfo { executables, ..debug_info }))
315}
316
317pub fn compile_prepared_db_program_artifact_for_functions<'db>(
330 db: &'db dyn CloneableDatabase,
331 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
332 compiler_config: CompilerConfig<'_>,
333) -> Result<ProgramArtifact> {
334 let mut sierra_program_with_debug =
335 get_sierra_program_for_functions(db, requested_function_ids)?.clone();
336
337 if compiler_config.replace_ids {
338 sierra_program_with_debug.program =
339 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
340 }
341
342 let mut annotations = Annotations::default();
343
344 if compiler_config.add_statements_functions {
345 annotations.extend(Annotations::from(
346 sierra_program_with_debug
347 .debug_info
348 .statements_locations
349 .extract_statements_functions(db),
350 ))
351 };
352
353 if compiler_config.add_statements_code_locations {
354 annotations.extend(Annotations::from(
355 sierra_program_with_debug
356 .debug_info
357 .statements_locations
358 .extract_statements_source_code_locations(db),
359 ))
360 };
361
362 if compiler_config.add_functions_debug_info {
363 annotations.extend(Annotations::from(
364 sierra_program_with_debug.debug_info.functions_info.extract_serializable_debug_info(db),
365 ))
366 }
367
368 let debug_info = DebugInfo {
369 type_names: Default::default(),
370 libfunc_names: Default::default(),
371 user_func_names: Default::default(),
372 annotations,
373 executables: Default::default(),
374 };
375
376 Ok(ProgramArtifact::stripped(sierra_program_with_debug.program).with_debug_info(debug_info))
377}