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()
94 .with_inlining_strategy(compiler_config.inlining_strategy)
95 .with_project_config(project_config.clone())
96 .build()?;
97 let main_crate_ids = get_main_crate_ids_from_project(&mut db, &project_config);
98
99 compile_prepared_db_program(&mut db, main_crate_ids, compiler_config)
100}
101
102pub fn compile_prepared_db_program(
114 db: &mut RootDatabase,
115 main_crate_ids: Vec<CrateId>,
116 compiler_config: CompilerConfig<'_>,
117) -> Result<Program> {
118 Ok(compile_prepared_db(db, main_crate_ids, compiler_config)?.program)
119}
120
121pub fn compile_prepared_db(
136 db: &RootDatabase,
137 main_crate_ids: Vec<CrateId>,
138 mut compiler_config: CompilerConfig<'_>,
139) -> Result<SierraProgramWithDebug> {
140 compiler_config.diagnostics_reporter.ensure(db)?;
141
142 let mut sierra_program_with_debug = Arc::unwrap_or_clone(
143 db.get_sierra_program(main_crate_ids)
144 .to_option()
145 .context("Compilation failed without any diagnostics")?,
146 );
147
148 if compiler_config.replace_ids {
149 sierra_program_with_debug.program =
150 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
151 }
152
153 Ok(sierra_program_with_debug)
154}
155
156fn warmup_db_blocking(
161 snapshot: salsa::Snapshot<RootDatabase>,
162 requested_function_ids: Vec<ConcreteFunctionWithBodyId>,
163) {
164 let processed_function_ids =
165 &Mutex::new(UnorderedHashSet::<ConcreteFunctionWithBodyId>::default());
166 rayon::scope(move |s| {
167 for func_id in requested_function_ids {
168 let snapshot = salsa::ParallelDatabase::snapshot(&*snapshot);
169
170 s.spawn(move |_| {
171 fn handle_func_inner(
172 processed_function_ids: &Mutex<UnorderedHashSet<ConcreteFunctionWithBodyId>>,
173 snapshot: salsa::Snapshot<RootDatabase>,
174 func_id: ConcreteFunctionWithBodyId,
175 ) {
176 if processed_function_ids.lock().unwrap().insert(func_id) {
177 rayon::scope(move |s| {
178 let db = &*snapshot;
179 let Ok(function) = db.function_with_body_sierra(func_id) else {
180 return;
181 };
182 for statement in &function.body {
183 let Some(related_function_id) =
184 try_get_function_with_body_id(db, statement)
185 else {
186 continue;
187 };
188
189 let snapshot = salsa::ParallelDatabase::snapshot(&*snapshot);
190 s.spawn(move |_| {
191 handle_func_inner(
192 processed_function_ids,
193 snapshot,
194 related_function_id,
195 )
196 })
197 }
198 });
199 }
200 }
201
202 handle_func_inner(processed_function_ids, snapshot, func_id)
203 });
204 }
205 });
206}
207
208fn spawn_warmup_db(db: &RootDatabase, requested_function_ids: Vec<ConcreteFunctionWithBodyId>) {
210 let snapshot = salsa::ParallelDatabase::snapshot(db);
211 rayon::spawn(move || warmup_db_blocking(snapshot, requested_function_ids));
212}
213
214pub fn get_sierra_program_for_functions(
217 db: &RootDatabase,
218 requested_function_ids: Vec<ConcreteFunctionWithBodyId>,
219 mut diagnostic_reporter: DiagnosticsReporter<'_>,
220) -> Result<Arc<SierraProgramWithDebug>> {
221 if rayon::current_num_threads() > 1 {
222 diagnostic_reporter.warm_up_diagnostics(db);
224 spawn_warmup_db(db, requested_function_ids.clone());
225 }
226
227 diagnostic_reporter.ensure(db)?;
228 db.get_sierra_program_for_functions(requested_function_ids)
229 .to_option()
230 .with_context(|| "Compilation failed without any diagnostics.")
231}
232
233pub fn compile_prepared_db_program_artifact(
248 db: &mut RootDatabase,
249 main_crate_ids: Vec<CrateId>,
250 mut compiler_config: CompilerConfig<'_>,
251) -> Result<ProgramArtifact> {
252 let add_statements_functions = compiler_config.add_statements_functions;
253 let add_statements_code_locations = compiler_config.add_statements_code_locations;
254
255 compiler_config.diagnostics_reporter.ensure(db)?;
256
257 let executable_functions = find_executable_function_ids(db, main_crate_ids.clone());
258
259 let mut sierra_program_with_debug = if executable_functions.is_empty() {
260 Arc::unwrap_or_clone(
263 db.get_sierra_program(main_crate_ids)
264 .to_option()
265 .context("Compilation failed without any diagnostics")?,
266 )
267 } else {
268 Arc::unwrap_or_clone(
270 db.get_sierra_program_for_functions(executable_functions.clone().into_keys().collect())
271 .to_option()
272 .context("Compilation failed without any diagnostics")?,
273 )
274 };
275
276 if compiler_config.replace_ids {
277 sierra_program_with_debug.program =
278 replace_sierra_ids_in_program(db, &sierra_program_with_debug.program);
279 }
280
281 let mut annotations = Annotations::default();
282
283 if add_statements_functions {
284 annotations.extend(Annotations::from(
285 sierra_program_with_debug
286 .debug_info
287 .statements_locations
288 .extract_statements_functions(db),
289 ))
290 };
291
292 if add_statements_code_locations {
293 annotations.extend(Annotations::from(
294 sierra_program_with_debug
295 .debug_info
296 .statements_locations
297 .extract_statements_source_code_locations(db),
298 ))
299 };
300
301 let debug_info = DebugInfo {
302 type_names: Default::default(),
303 libfunc_names: Default::default(),
304 user_func_names: Default::default(),
305 annotations,
306 executables: Default::default(),
307 };
308
309 let executables =
311 collect_executables(db, executable_functions, &sierra_program_with_debug.program);
312
313 Ok(ProgramArtifact::stripped(sierra_program_with_debug.program)
314 .with_debug_info(DebugInfo { executables, ..debug_info }))
315}