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