From babbb5a6145292d99400ed6d10e8aa0716eee14a Mon Sep 17 00:00:00 2001 From: Piotr Magiera <56825108+piotmag769@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:30:44 +0100 Subject: [PATCH] Add statements->functions map to `SierraDebugInfo` (#5325) --- Cargo.lock | 2 ++ crates/bin/cairo-run/src/main.rs | 2 +- crates/cairo-lang-compiler/src/lib.rs | 14 ++++------- .../cairo-lang-runner/src/profiling_test.rs | 3 ++- crates/cairo-lang-sierra-generator/Cargo.toml | 2 ++ crates/cairo-lang-sierra-generator/src/lib.rs | 1 + .../src/statements_functions.rs | 24 +++++++++++++++++++ .../src/statements_locations.rs | 20 +++++++++++++--- .../src/contract_class.rs | 8 ++++++- crates/cairo-lang-starknet/src/compile.rs | 12 +++++++++- crates/cairo-lang-starknet/src/test_utils.rs | 1 + crates/cairo-lang-test-plugin/src/lib.rs | 3 ++- 12 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 crates/cairo-lang-sierra-generator/src/statements_functions.rs diff --git a/Cargo.lock b/Cargo.lock index e8bf850294f..4e44fb201f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -836,6 +836,8 @@ dependencies = [ "once_cell", "pretty_assertions", "salsa", + "serde", + "serde_json", "smol_str", "test-case", "test-log", diff --git a/crates/bin/cairo-run/src/main.rs b/crates/bin/cairo-run/src/main.rs index 925ccdba4ad..618e7248ef9 100644 --- a/crates/bin/cairo-run/src/main.rs +++ b/crates/bin/cairo-run/src/main.rs @@ -97,7 +97,7 @@ fn main() -> anyhow::Result<()> { let profiling_info_processor = ProfilingInfoProcessor::new( Some(db), sierra_program, - debug_info.statements_locations.get_statements_functions_map(db), + debug_info.statements_locations.get_statements_functions_map_for_tests(db), ); match result.profiling_info { Some(raw_profiling_info) => { diff --git a/crates/cairo-lang-compiler/src/lib.rs b/crates/cairo-lang-compiler/src/lib.rs index 3463b69e6d9..8d62d952f20 100644 --- a/crates/cairo-lang-compiler/src/lib.rs +++ b/crates/cairo-lang-compiler/src/lib.rs @@ -22,6 +22,7 @@ pub mod diagnostics; pub mod project; /// Configuration for the compiler. +#[derive(Default)] pub struct CompilerConfig<'c> { pub diagnostics_reporter: DiagnosticsReporter<'c>, @@ -31,17 +32,10 @@ pub struct CompilerConfig<'c> { /// The name of the allowed libfuncs list to use in compilation. /// If None the default list of audited libfuncs will be used. pub allowed_libfuncs_list_name: Option, -} -/// The default compiler configuration. -impl Default for CompilerConfig<'static> { - fn default() -> Self { - CompilerConfig { - diagnostics_reporter: DiagnosticsReporter::default(), - replace_ids: false, - allowed_libfuncs_list_name: None, - } - } + /// Adds mapping used by [cairo-profiler](https://github.com/software-mansion/cairo-profiler) to + /// [cairo_lang_sierra::debug_info::Annotations] in [cairo_lang_sierra::debug_info::DebugInfo]. + pub add_statements_functions: bool, } /// Compiles a Cairo project at the given path. diff --git a/crates/cairo-lang-runner/src/profiling_test.rs b/crates/cairo-lang-runner/src/profiling_test.rs index 0409f4d61ee..e4765448a92 100644 --- a/crates/cairo-lang-runner/src/profiling_test.rs +++ b/crates/cairo-lang-runner/src/profiling_test.rs @@ -48,7 +48,8 @@ pub fn test_profiling( let SierraProgramWithDebug { program: sierra_program, debug_info } = Arc::unwrap_or_clone(db.get_sierra_program(vec![test_module.crate_id]).unwrap()); let sierra_program = replace_sierra_ids_in_program(&db, &sierra_program); - let statements_functions = debug_info.statements_locations.get_statements_functions_map(&db); + let statements_functions = + debug_info.statements_locations.get_statements_functions_map_for_tests(&db); let runner = SierraCasmRunner::new( sierra_program.clone(), Some(Default::default()), diff --git a/crates/cairo-lang-sierra-generator/Cargo.toml b/crates/cairo-lang-sierra-generator/Cargo.toml index c0e7f72914f..49e6963eddb 100644 --- a/crates/cairo-lang-sierra-generator/Cargo.toml +++ b/crates/cairo-lang-sierra-generator/Cargo.toml @@ -25,6 +25,8 @@ itertools = { workspace = true, default-features = true } num-traits = { workspace = true } once_cell.workspace = true salsa.workspace = true +serde.workspace = true +serde_json.workspace = true smol_str.workspace = true [dev-dependencies] diff --git a/crates/cairo-lang-sierra-generator/src/lib.rs b/crates/cairo-lang-sierra-generator/src/lib.rs index 7f5594c1a11..ccf89e5b441 100644 --- a/crates/cairo-lang-sierra-generator/src/lib.rs +++ b/crates/cairo-lang-sierra-generator/src/lib.rs @@ -20,6 +20,7 @@ pub mod program_generator; pub mod replace_ids; mod resolve_labels; mod specialization_context; +pub mod statements_functions; pub mod statements_locations; mod store_variables; #[cfg(any(feature = "testing", test))] diff --git a/crates/cairo-lang-sierra-generator/src/statements_functions.rs b/crates/cairo-lang-sierra-generator/src/statements_functions.rs new file mode 100644 index 00000000000..56d931dc5b0 --- /dev/null +++ b/crates/cairo-lang-sierra-generator/src/statements_functions.rs @@ -0,0 +1,24 @@ +use std::collections::HashMap; + +use cairo_lang_sierra::debug_info::Annotations; +use cairo_lang_sierra::program::StatementIdx; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use serde::{Deserialize, Serialize}; + +/// The mapping from sierra statement index to fully qualified Cairo path of the Cairo function +/// (if obtainable) which caused the statement to be generated. Should be created using +/// [`crate::statements_locations::StatementsLocations::extract_statements_functions`]. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct StatementsFunctions { + pub statements_to_functions_map: HashMap>, +} + +impl From for Annotations { + fn from(value: StatementsFunctions) -> Self { + let mapping = serde_json::to_value(value.statements_to_functions_map).unwrap(); + OrderedHashMap::from([( + "github.com/software-mansion/cairo-profiler".to_string(), + serde_json::Value::from_iter([("statements_functions", mapping)]), + )]) + } +} diff --git a/crates/cairo-lang-sierra-generator/src/statements_locations.rs b/crates/cairo-lang-sierra-generator/src/statements_locations.rs index 85865229e7e..982450a7c34 100644 --- a/crates/cairo-lang-sierra-generator/src/statements_locations.rs +++ b/crates/cairo-lang-sierra-generator/src/statements_locations.rs @@ -9,6 +9,8 @@ use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; use itertools::Itertools; +use crate::statements_functions::StatementsFunctions; + #[cfg(test)] #[path = "statements_locations_test.rs"] mod test; @@ -138,14 +140,26 @@ impl StatementsLocations { Self { locations } } /// Builds a map between each Sierra statement index and a string representation of the Cairo - /// function that it was generated from. The function representation is composed of the function - /// name and the path (modules and impls) to the function in the file. It is used for places + /// function that it was generated from. It is used for places /// without db access such as the profiler. // TODO(Gil): Add a db access to the profiler and remove this function. - pub fn get_statements_functions_map( + pub fn get_statements_functions_map_for_tests( &self, db: &dyn DefsGroup, ) -> UnorderedHashMap { self.locations.map(|s| containing_function_identifier_for_tests(db, *s)) } + + /// Creates a new [StatementsFunctions] struct using [StatementsLocations] and [DefsGroup]. + pub fn extract_statements_functions(&self, db: &dyn DefsGroup) -> StatementsFunctions { + StatementsFunctions { + statements_to_functions_map: self + .locations + .iter_sorted() + .map(|(statement_idx, stable_location)| { + (*statement_idx, containing_function_identifier(db, *stable_location)) + }) + .collect(), + } + } } diff --git a/crates/cairo-lang-starknet-classes/src/contract_class.rs b/crates/cairo-lang-starknet-classes/src/contract_class.rs index 8d559ed6ccb..eb91ec9a508 100644 --- a/crates/cairo-lang-starknet-classes/src/contract_class.rs +++ b/crates/cairo-lang-starknet-classes/src/contract_class.rs @@ -1,7 +1,9 @@ use cairo_lang_sierra as sierra; use cairo_lang_utils::bigint::{deserialize_big_uint, serialize_big_uint, BigUintAsHex}; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; +use serde_json::Value; use thiserror::Error; use crate::abi::Contract; @@ -36,14 +38,18 @@ impl ContractClass { program: &sierra::program::Program, entry_points_by_type: ContractEntryPoints, abi: Option, + annotations: OrderedHashMap, ) -> Result { + let mut sierra_program_debug_info = sierra::debug_info::DebugInfo::extract(program); + sierra_program_debug_info.annotations.extend(annotations); + Ok(Self { sierra_program: sierra_to_felt252s( current_sierra_version_id(), current_compiler_version_id(), program, )?, - sierra_program_debug_info: Some(sierra::debug_info::DebugInfo::extract(program)), + sierra_program_debug_info: Some(sierra_program_debug_info), contract_class_version: DEFAULT_CONTRACT_CLASS_VERSION.into(), entry_points_by_type, abi, diff --git a/crates/cairo-lang-starknet/src/compile.rs b/crates/cairo-lang-starknet/src/compile.rs index 7432404b3e4..cbdfeb709a9 100644 --- a/crates/cairo-lang-starknet/src/compile.rs +++ b/crates/cairo-lang-starknet/src/compile.rs @@ -10,6 +10,7 @@ use cairo_lang_diagnostics::ToOption; use cairo_lang_filesystem::ids::CrateId; use cairo_lang_lowering::db::LoweringGroup; use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId; +use cairo_lang_sierra::debug_info::Annotations; use cairo_lang_sierra_generator::canonical_id_replacer::CanonicalReplacer; use cairo_lang_sierra_generator::db::SierraGenGroup; use cairo_lang_sierra_generator::program_generator::SierraProgramWithDebug; @@ -126,7 +127,7 @@ fn compile_contract_with_prepared_and_checked_db( ) -> Result { let SemanticEntryPoints { external, l1_handler, constructor } = extract_semantic_entrypoints(db, contract)?; - let SierraProgramWithDebug { program: mut sierra_program, .. } = Arc::unwrap_or_clone( + let SierraProgramWithDebug { program: mut sierra_program, debug_info } = Arc::unwrap_or_clone( db.get_sierra_program_for_functions( chain!(&external, &l1_handler, &constructor).map(|f| f.value).collect(), ) @@ -146,6 +147,14 @@ fn compile_contract_with_prepared_and_checked_db( // Later generation of ABI verifies that there is up to one constructor. constructor: get_entry_points(db, &constructor, &replacer)?, }; + + let annotations = if compiler_config.add_statements_functions { + let statements_functions = debug_info.statements_locations.extract_statements_functions(db); + Annotations::from(statements_functions) + } else { + Default::default() + }; + let contract_class = ContractClass::new( &sierra_program, entry_points_by_type, @@ -156,6 +165,7 @@ fn compile_contract_with_prepared_and_checked_db( .finalize() .with_context(|| "Could not create ABI from contract submodule")?, ), + annotations, )?; contract_class.sanity_check(); Ok(contract_class) diff --git a/crates/cairo-lang-starknet/src/test_utils.rs b/crates/cairo-lang-starknet/src/test_utils.rs index 1d93da5f6fa..165e9c1f4cb 100644 --- a/crates/cairo-lang-starknet/src/test_utils.rs +++ b/crates/cairo-lang-starknet/src/test_utils.rs @@ -82,6 +82,7 @@ pub fn get_test_contract(example_file_name: &str) -> ContractClass { replace_ids: true, allowed_libfuncs_list_name: Some(BUILTIN_ALL_LIBFUNCS_LIST.to_string()), diagnostics_reporter, + add_statements_functions: false, }, ) .expect("compile_path failed") diff --git a/crates/cairo-lang-test-plugin/src/lib.rs b/crates/cairo-lang-test-plugin/src/lib.rs index d30a9c9534a..71cd7732489 100644 --- a/crates/cairo-lang-test-plugin/src/lib.rs +++ b/crates/cairo-lang-test-plugin/src/lib.rs @@ -100,7 +100,8 @@ pub fn compile_test_prepared_db( ); let replacer = DebugReplacer { db }; let sierra_program = replacer.apply(&sierra_program); - let statements_functions = debug_info.statements_locations.get_statements_functions_map(db); + let statements_functions = + debug_info.statements_locations.get_statements_functions_map_for_tests(db); let named_tests = all_tests .into_iter()