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 pub add_functions_debug_info: bool,
60}
61
62pub fn compile_cairo_project_at_path(
73 path: &Path,
74 compiler_config: CompilerConfig<'_>,
75 inlining_strategy: InliningStrategy,
76) -> Result<Program> {
77 let mut db = RootDatabase::builder()
78 .with_optimizations(Optimizations::enabled_with_default_movable_functions(
79 inlining_strategy,
80 ))
81 .detect_corelib()
82 .build()?;
83 let main_crate_ids = setup_project(&mut db, path)?;
84 compile_prepared_db_program(
85 &db,
86 CrateInput::into_crate_ids(&db, main_crate_ids),
87 compiler_config,
88 )
89}
90
91pub fn compile(
101 project_config: ProjectConfig,
102 compiler_config: CompilerConfig<'_>,
103) -> Result<Program> {
104 let db = RootDatabase::builder()
105 .with_optimizations(Optimizations::enabled_with_default_movable_functions(
106 InliningStrategy::Default,
107 ))
108 .with_project_config(project_config.clone())
109 .build()?;
110 let main_crate_ids = get_main_crate_ids_from_project(&db, &project_config);
111
112 compile_prepared_db_program(&db, main_crate_ids, compiler_config)
113}
114
115pub fn compile_prepared_db_program<'db>(
127 db: &'db dyn Database,
128 main_crate_ids: Vec<CrateId<'db>>,
129 compiler_config: CompilerConfig<'_>,
130) -> Result<Program> {
131 Ok(compile_prepared_db(db, main_crate_ids, compiler_config)?.program)
132}
133
134pub fn compile_prepared_db<'db>(
149 db: &'db dyn Database,
150 main_crate_ids: Vec<CrateId<'db>>,
151 mut compiler_config: CompilerConfig<'_>,
152) -> Result<SierraProgramWithDebug<'db>> {
153 compiler_config.diagnostics_reporter.ensure(db)?;
154
155 let mut sierra_program_with_debug = db
156 .get_sierra_program(main_crate_ids)
157 .to_option()
158 .context("Compilation failed without any diagnostics")?
159 .clone();
160
161 if compiler_config.replace_ids {
162 sierra_program_with_debug.program =
163 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
164 }
165
166 Ok(sierra_program_with_debug)
167}
168
169fn should_warmup() -> bool {
171 rayon::current_num_threads() > 1
172}
173
174pub fn ensure_diagnostics(
185 db: &dyn Database,
186 diagnostic_reporter: &mut DiagnosticsReporter<'_>,
187) -> std::result::Result<(), DiagnosticsError> {
188 if should_warmup() {
189 let crates = diagnostic_reporter.crates_of_interest(db);
190 join(db, |db| warmup_diagnostics_blocking(db, crates), |db| diagnostic_reporter.ensure(db))
191 .1
192 } else {
193 diagnostic_reporter.ensure(db)
194 }
195}
196
197fn warmup_diagnostics_blocking(db: &dyn Database, crates: Vec<CrateInput>) {
200 let _: () = par_map(db, crates, |db, crate_input| {
201 let crate_id = crate_input.into_crate_long_id(db).intern(db);
202 let crate_modules = db.crate_modules(crate_id);
203 let _: () = par_map(db, crate_modules, |db, module_id| {
204 for file_id in db.module_files(*module_id).unwrap_or_default().iter().copied() {
205 db.file_syntax_diagnostics(file_id);
206 }
207 let _ = db.module_semantic_diagnostics(*module_id);
208 let _ = db.module_lowering_diagnostics(*module_id);
209 });
210 });
211}
212
213fn warmup_functions_blocking<'db>(
218 db: &dyn Database,
219 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
220) {
221 let processed_function_ids = &Mutex::new(UnorderedHashSet::<salsa::Id>::default());
222 let _: () = par_map(db, requested_function_ids, move |db, func_id| {
223 fn handle_func_inner<'db>(
224 processed_function_ids: &Mutex<UnorderedHashSet<salsa::Id>>,
225 snapshot: &dyn Database,
226 func_id: ConcreteFunctionWithBodyId<'db>,
227 ) {
228 if processed_function_ids.lock().unwrap().insert(func_id.as_intern_id()) {
229 let Ok(function) = snapshot.function_with_body_sierra(func_id) else {
230 return;
231 };
232 let _: () = par_map(snapshot, &function.body, move |snapshot, statement| {
233 let related_function_id: ConcreteFunctionWithBodyId<'_> =
234 if let Some(r_id) = try_get_function_with_body_id(snapshot, statement) {
235 r_id
236 } else {
237 return;
238 };
239
240 handle_func_inner(processed_function_ids, snapshot, related_function_id);
241 });
242 }
243 }
244 handle_func_inner(processed_function_ids, db, func_id)
245 });
246}
247
248pub fn get_sierra_program_for_functions<'db>(
251 db: &'db dyn Database,
252 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
253) -> Result<&'db SierraProgramWithDebug<'db>> {
254 if should_warmup() {
255 let requested_function_ids = requested_function_ids.clone();
256 warmup_functions_blocking(db, requested_function_ids);
257 }
258 db.get_sierra_program_for_functions(requested_function_ids)
259 .to_option()
260 .context("Compilation failed without any diagnostics.")
261}
262
263pub fn compile_prepared_db_program_artifact<'db>(
278 db: &'db dyn Database,
279 main_crate_ids: Vec<CrateId<'db>>,
280 mut compiler_config: CompilerConfig<'_>,
281) -> Result<ProgramArtifact> {
282 ensure_diagnostics(db, &mut compiler_config.diagnostics_reporter)?;
283
284 let executable_functions = find_executable_function_ids(db, main_crate_ids.clone());
285
286 let function_ids = if executable_functions.is_empty() {
287 find_all_free_function_ids(db, main_crate_ids)
290 .to_option()
291 .context("Compilation failed without any diagnostics.")?
292 } else {
293 executable_functions.clone().into_keys().collect()
295 };
296
297 let mut program_artifact =
298 compile_prepared_db_program_artifact_for_functions(db, function_ids, compiler_config)?;
299
300 let executables = collect_executables(db, executable_functions, &program_artifact.program);
302
303 let debug_info = program_artifact.debug_info.take().unwrap_or_default();
304
305 Ok(program_artifact.with_debug_info(DebugInfo { executables, ..debug_info }))
306}
307
308pub fn compile_prepared_db_program_artifact_for_functions<'db>(
321 db: &'db dyn Database,
322 requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
323 compiler_config: CompilerConfig<'_>,
324) -> Result<ProgramArtifact> {
325 let mut sierra_program_with_debug =
326 get_sierra_program_for_functions(db, requested_function_ids)?.clone();
327
328 if compiler_config.replace_ids {
329 sierra_program_with_debug.program =
330 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
331 }
332
333 let mut annotations = Annotations::default();
334
335 if compiler_config.add_statements_functions {
336 annotations.extend(Annotations::from(
337 sierra_program_with_debug
338 .debug_info
339 .statements_locations
340 .extract_statements_functions(db),
341 ))
342 };
343
344 if compiler_config.add_statements_code_locations {
345 annotations.extend(Annotations::from(
346 sierra_program_with_debug
347 .debug_info
348 .statements_locations
349 .extract_statements_source_code_locations(db),
350 ))
351 };
352
353 if compiler_config.add_functions_debug_info {
354 annotations.extend(Annotations::from(
355 sierra_program_with_debug.debug_info.functions_info.extract_serializable_debug_info(db),
356 ))
357 }
358
359 let debug_info = DebugInfo {
360 type_names: Default::default(),
361 libfunc_names: Default::default(),
362 user_func_names: Default::default(),
363 annotations,
364 executables: Default::default(),
365 };
366
367 Ok(ProgramArtifact::stripped(sierra_program_with_debug.program).with_debug_info(debug_info))
368}