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::Intern;
27use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
28use salsa::{Database, join, par_map};
29
30use crate::db::RootDatabase;
31use crate::diagnostics::{DiagnosticsError, DiagnosticsReporter};
32use crate::project::{ProjectConfig, get_main_crate_ids_from_project, setup_project};
33
34pub mod db;
35pub mod diagnostics;
36pub mod project;
37
38#[cfg(test)]
39mod test;
40
41#[derive(Default)]
43pub struct CompilerConfig<'a> {
44 pub diagnostics_reporter: DiagnosticsReporter<'a>,
45
46 pub replace_ids: bool,
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 inlining_strategy: InliningStrategy,
72) -> Result<Program> {
73 let mut db = RootDatabase::builder()
74 .with_optimizations(Optimizations::enabled_with_default_movable_functions(
75 inlining_strategy,
76 ))
77 .detect_corelib()
78 .build()?;
79 let main_crate_ids = setup_project(&mut db, path)?;
80 compile_prepared_db_program(
81 &db,
82 CrateInput::into_crate_ids(&db, main_crate_ids),
83 compiler_config,
84 )
85}
86
87pub fn compile(
97 project_config: ProjectConfig,
98 compiler_config: CompilerConfig<'_>,
99) -> Result<Program> {
100 let db = RootDatabase::builder()
101 .with_optimizations(Optimizations::enabled_with_default_movable_functions(
102 InliningStrategy::Default,
103 ))
104 .with_project_config(project_config.clone())
105 .build()?;
106 let main_crate_ids = get_main_crate_ids_from_project(&db, &project_config);
107
108 compile_prepared_db_program(&db, main_crate_ids, compiler_config)
109}
110
111pub fn compile_prepared_db_program<'db>(
123 db: &'db dyn Database,
124 main_crate_ids: Vec<CrateId<'db>>,
125 compiler_config: CompilerConfig<'_>,
126) -> Result<Program> {
127 Ok(compile_prepared_db(db, main_crate_ids, compiler_config)?.program)
128}
129
130pub fn compile_prepared_db<'db>(
145 db: &'db dyn Database,
146 main_crate_ids: Vec<CrateId<'db>>,
147 mut compiler_config: CompilerConfig<'_>,
148) -> Result<SierraProgramWithDebug<'db>> {
149 compiler_config.diagnostics_reporter.ensure(db)?;
150
151 let mut sierra_program_with_debug = db
152 .get_sierra_program(main_crate_ids)
153 .to_option()
154 .context("Compilation failed without any diagnostics")?
155 .clone();
156
157 if compiler_config.replace_ids {
158 sierra_program_with_debug.program =
159 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
160 }
161
162 Ok(sierra_program_with_debug)
163}
164
165fn should_warmup() -> bool {
167 rayon::current_num_threads() > 1
168}
169
170pub fn ensure_diagnostics(
181 db: &dyn Database,
182 diagnostic_reporter: &mut DiagnosticsReporter<'_>,
183) -> std::result::Result<(), DiagnosticsError> {
184 if should_warmup() {
185 let crates = diagnostic_reporter.crates_of_interest(db);
186 join(db, |db| warmup_diagnostics_blocking(db, crates), |db| diagnostic_reporter.ensure(db))
187 .1
188 } else {
189 diagnostic_reporter.ensure(db)
190 }
191}
192
193fn warmup_diagnostics_blocking(db: &dyn Database, crates: Vec<CrateInput>) {
196 let _: () = par_map(db, crates, |db, crate_input| {
197 let crate_id = crate_input.into_crate_long_id(db).intern(db);
198 let crate_modules = db.crate_modules(crate_id);
199 let _: () = par_map(db, crate_modules, |db, module_id| {
200 for file_id in db.module_files(*module_id).unwrap_or_default().iter().copied() {
201 db.file_syntax_diagnostics(file_id);
202 }
203 let _ = db.module_semantic_diagnostics(*module_id);
204 let _ = db.module_lowering_diagnostics(*module_id);
205 });
206 });
207}
208
209fn warmup_functions_blocking<'db>(
214 db: &dyn Database,
215 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
216) {
217 let processed_function_ids = &Mutex::new(UnorderedHashSet::<salsa::Id>::default());
218 let _: () = par_map(db, requested_function_ids, move |db, func_id| {
219 fn handle_func_inner<'db>(
220 processed_function_ids: &Mutex<UnorderedHashSet<salsa::Id>>,
221 snapshot: &dyn Database,
222 func_id: ConcreteFunctionWithBodyId<'db>,
223 ) {
224 if processed_function_ids.lock().unwrap().insert(func_id.as_intern_id()) {
225 let Ok(function) = snapshot.function_with_body_sierra(func_id) else {
226 return;
227 };
228 let _: () = par_map(snapshot, &function.body, move |snapshot, statement| {
229 let related_function_id: ConcreteFunctionWithBodyId<'_> =
230 if let Some(r_id) = try_get_function_with_body_id(snapshot, statement) {
231 r_id
232 } else {
233 return;
234 };
235
236 handle_func_inner(processed_function_ids, snapshot, related_function_id);
237 });
238 }
239 }
240 handle_func_inner(processed_function_ids, db, func_id)
241 });
242}
243
244pub fn get_sierra_program_for_functions<'db>(
247 db: &'db dyn Database,
248 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
249) -> Result<&'db SierraProgramWithDebug<'db>> {
250 if should_warmup() {
251 let requested_function_ids = requested_function_ids.clone();
252 warmup_functions_blocking(db, requested_function_ids);
253 }
254 db.get_sierra_program_for_functions(requested_function_ids)
255 .to_option()
256 .context("Compilation failed without any diagnostics.")
257}
258
259pub fn compile_prepared_db_program_artifact<'db>(
274 db: &'db dyn Database,
275 main_crate_ids: Vec<CrateId<'db>>,
276 mut compiler_config: CompilerConfig<'_>,
277) -> Result<ProgramArtifact> {
278 ensure_diagnostics(db, &mut compiler_config.diagnostics_reporter)?;
279
280 let executable_functions = find_executable_function_ids(db, main_crate_ids.clone());
281
282 let function_ids = if executable_functions.is_empty() {
283 find_all_free_function_ids(db, main_crate_ids)
286 .to_option()
287 .context("Compilation failed without any diagnostics.")?
288 } else {
289 executable_functions.clone().into_keys().collect()
291 };
292
293 let mut program_artifact =
294 compile_prepared_db_program_artifact_for_functions(db, function_ids, compiler_config)?;
295
296 let executables = collect_executables(db, executable_functions, &program_artifact.program);
298
299 let debug_info = program_artifact.debug_info.take().unwrap_or_default();
300
301 Ok(program_artifact.with_debug_info(DebugInfo { executables, ..debug_info }))
302}
303
304pub fn compile_prepared_db_program_artifact_for_functions<'db>(
317 db: &'db dyn Database,
318 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
319 compiler_config: CompilerConfig<'_>,
320) -> Result<ProgramArtifact> {
321 let mut sierra_program_with_debug =
322 get_sierra_program_for_functions(db, requested_function_ids)?.clone();
323
324 if compiler_config.replace_ids {
325 sierra_program_with_debug.program =
326 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
327 }
328
329 let mut annotations = Annotations::default();
330
331 if compiler_config.add_statements_functions {
332 annotations.extend(Annotations::from(
333 sierra_program_with_debug
334 .debug_info
335 .statements_locations
336 .extract_statements_functions(db),
337 ))
338 };
339
340 if compiler_config.add_statements_code_locations {
341 annotations.extend(Annotations::from(
342 sierra_program_with_debug
343 .debug_info
344 .statements_locations
345 .extract_statements_source_code_locations(db),
346 ))
347 };
348
349 let debug_info = DebugInfo {
350 type_names: Default::default(),
351 libfunc_names: Default::default(),
352 user_func_names: Default::default(),
353 annotations,
354 executables: Default::default(),
355 };
356
357 Ok(ProgramArtifact::stripped(sierra_program_with_debug.program).with_debug_info(debug_info))
358}