From 78829294f25cb0a8b83a4b3f8da90cd29649888c Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 23 Jun 2025 09:28:12 -0300 Subject: [PATCH 01/21] fix ci --- .github/workflows/starknet-blocks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/starknet-blocks.yml b/.github/workflows/starknet-blocks.yml index b89f5bd53..df95492be 100644 --- a/.github/workflows/starknet-blocks.yml +++ b/.github/workflows/starknet-blocks.yml @@ -89,12 +89,12 @@ jobs: - name: Run with Native if: ${{ matrix.runner == 'native' }} run: | - cargo run --features state_dump block mainnet ${{ matrix.block }} + cargo run --release --bin replay --features state_dump block mainnet ${{ matrix.block }} - name: Run with VM if: ${{ matrix.runner == 'vm' }} run: | - cargo run --features "state_dump,only_cairo_vm" block mainnet ${{ matrix.block }} + cargo run --release --bin replay --features "state_dump,only_cairo_vm" block mainnet ${{ matrix.block }} - name: Upload dumps uses: actions/upload-artifact@v4 From b54592b683fc9129e8853fb1149a78bea7f250d7 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 23 Jun 2025 09:31:59 -0300 Subject: [PATCH 02/21] fix daily blocks ci --- .github/workflows/daily.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index 0ceea0b10..817c161b1 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -148,13 +148,13 @@ jobs: run: | BLOCK_START=${{ matrix.block }} BLOCK_END=$(($BLOCK_START + $RANGE_SIZE - 1)) - cargo run --release --features state_dump block-range $BLOCK_START $BLOCK_END mainnet + cargo run --release --bin replay --features state_dump block-range $BLOCK_START $BLOCK_END mainnet - name: Run with VM if: ${{ matrix.runner == 'vm' }} run: | BLOCK_START=${{ matrix.block }} BLOCK_END=$(($BLOCK_START + $RANGE_SIZE - 1)) - cargo run --release --features state_dump,only_cairo_vm block-range $BLOCK_START $BLOCK_END mainnet + cargo run --release --bin replay --features state_dump,only_cairo_vm block-range $BLOCK_START $BLOCK_END mainnet # We always upload the dump, even if the job fails - name: Upload dumps From 4478643c0e4f4eb02fda435a7fa66a2da22f25bf Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 23 Jun 2025 09:35:47 -0300 Subject: [PATCH 03/21] update starknet-replay and sequencer in staknet-blocks workflow --- .github/workflows/starknet-blocks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/starknet-blocks.yml b/.github/workflows/starknet-blocks.yml index df95492be..63a4b45df 100644 --- a/.github/workflows/starknet-blocks.yml +++ b/.github/workflows/starknet-blocks.yml @@ -32,7 +32,7 @@ jobs: with: repository: lambdaclass/starknet-replay path: starknet-replay - ref: d36491aa5fca3f48b4d7fb25eba599603ff48225 + ref: 95c7e85f65acbf536462ffb538b866ddafb7ce39 # We need native to use the linux deps ci action - name: Checkout Native uses: actions/checkout@v4 @@ -43,7 +43,7 @@ jobs: with: repository: lambdaclass/sequencer path: sequencer - ref: 14be65ca995ac702bad26ac20f2a522d9515f70a + ref: 7aaf0e38c2c80276b8121bca5df8e8389bcdc2f6 - name: Cache RPC Calls uses: actions/cache@v4.2.0 with: From 7bcb98d3b3ff441a9738d4f7533ec0d4b6cdd86c Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 1 Jul 2025 12:04:30 -0300 Subject: [PATCH 04/21] implement libfunc_counter --- Cargo.toml | 1 + src/bin/cairo-native-run.rs | 72 +++++++- src/compiler.rs | 28 ++++ src/executor/aot.rs | 3 + src/executor/contract.rs | 3 + src/executor/jit.rs | 3 + src/metadata.rs | 1 + src/metadata/libfunc_counter.rs | 282 ++++++++++++++++++++++++++++++++ 8 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 src/metadata/libfunc_counter.rs diff --git a/Cargo.toml b/Cargo.toml index 08b46bc6b..b5873fed5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ with-cheatcode = [] with-debug-utils = [] with-mem-tracing = [] with-libfunc-profiling = [] +with-libfunc-counter = [] with-segfault-catcher = [] with-trace-dump = ["dep:sierra-emu"] diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index ce40390b2..e73479abc 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -15,7 +15,7 @@ use cairo_native::{ starknet_stub::StubSyscallHandler, }; use clap::{Parser, ValueEnum}; -#[cfg(feature = "with-libfunc-profiling")] +#[cfg(any(feature = "with-libfunc-profiling", feature = "with-libfunc-counter"))] use std::collections::HashMap; use std::path::PathBuf; use tracing_subscriber::{EnvFilter, FmtSubscriber}; @@ -57,6 +57,11 @@ struct Args { /// The output path for the libfunc profilling results profiler_output: Option, + #[cfg(feature = "with-libfunc-counter")] + #[arg(long)] + /// The output path for the execution trace + libfunc_counter_output: Option, + #[cfg(feature = "with-trace-dump")] #[arg(long)] /// The output path for the execution trace @@ -120,6 +125,17 @@ fn main() -> anyhow::Result<()> { } } + #[cfg(feature = "with-libfunc-counter")] + { + use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; + if let Some(counter_id) = + executor.find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) + { + let counter_id = counter_id.cast::(); + unsafe { *counter_id = 0 }; + } + } + Box::new(move |function_id, args, gas, syscall_handler| { executor.invoke_dynamic_with_syscall_handler( function_id, @@ -154,6 +170,17 @@ fn main() -> anyhow::Result<()> { } } + #[cfg(feature = "with-libfunc-counter")] + { + use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; + if let Some(counter_id) = + executor.find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) + { + let counter_id = counter_id.cast::(); + unsafe { *counter_id = 0 }; + } + } + Box::new(move |function_id, args, gas, syscall_handler| { executor.invoke_dynamic_with_syscall_handler( function_id, @@ -186,6 +213,18 @@ fn main() -> anyhow::Result<()> { .insert(0, ProfilerImpl::new()); } + #[cfg(feature = "with-libfunc-counter")] + { + use cairo_native::metadata::libfunc_counter::libfunc_counter_runtime::{ + CounterImpl, LIBFUNC_COUNTER, + }; + + LIBFUNC_COUNTER.lock().unwrap().insert( + 0, + CounterImpl::new(sierra_program.libfunc_declarations.len()), + ); + } + let gas_metadata = GasMetadata::new(&sierra_program, Some(MetadataComputationConfig::default())).unwrap(); @@ -281,6 +320,37 @@ fn main() -> anyhow::Result<()> { } } + #[cfg(feature = "with-libfunc-counter")] + if let Some(libfunc_counter_output) = args.libfunc_counter_output { + use std::collections::HashMap; + + let counters = + cairo_native::metadata::libfunc_counter::libfunc_counter_runtime::LIBFUNC_COUNTER + .lock() + .unwrap(); + assert_eq!(counters.len(), 1); + + let libfunc_counter = counters.values().next().unwrap(); + + let libfunc_counts = libfunc_counter + .array_counter + .iter() + .enumerate() + .map(|(i, count)| { + let libfunc = &sierra_program.libfunc_declarations[i]; + let debug_name = libfunc.id.debug_name.clone().unwrap().to_string(); + + (debug_name, *count) + }) + .collect::>(); + dbg!(&libfunc_counts); + serde_json::to_writer_pretty( + std::fs::File::create(libfunc_counter_output).unwrap(), + &libfunc_counts, + ) + .unwrap(); + } + #[cfg(feature = "with-trace-dump")] if let Some(trace_output) = args.trace_output { let traces = cairo_native::metadata::trace_dump::trace_dump_runtime::TRACE_DUMP diff --git a/src/compiler.rs b/src/compiler.rs index cecf22f5f..776aaa25d 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -60,6 +60,8 @@ use crate::{ utils::{generate_function_name, walk_ir::walk_mlir_block, BlockExt}, }; use bumpalo::Bump; +#[cfg(feature = "with-libfunc-counter")] +use cairo_lang_sierra::ids::ConcreteLibfuncId; use cairo_lang_sierra::{ edit_state, extensions::{ @@ -151,6 +153,14 @@ pub fn compile( let n_libfuncs = program.libfunc_declarations.len() + 1; let sierra_stmt_start_offset = num_types + n_libfuncs + 1; + #[cfg(feature = "with-libfunc-counter")] + let libfunc_indexes = program + .libfunc_declarations + .iter() + .enumerate() + .map(|(idx, libf)| (libf.id.clone(), idx)) + .collect::>(); + for function in &program.funcs { tracing::info!("Compiling function `{}`.", function.id); compile_func( @@ -159,6 +169,8 @@ pub fn compile( registry, function, &program.statements, + #[cfg(feature = "with-libfunc-counter")] + &libfunc_indexes, metadata, di_compile_unit_id, sierra_stmt_start_offset, @@ -186,6 +198,7 @@ fn compile_func( registry: &ProgramRegistry, function: &Function, statements: &[Statement], + #[cfg(feature = "with-libfunc-counter")] libfunc_indexes: &HashMap, metadata: &mut MetadataStorage, di_compile_unit_id: Attribute, sierra_stmt_start_offset: usize, @@ -640,6 +653,21 @@ fn compile_func( }, }; + #[cfg(feature = "with-libfunc-counter")] + { + // Can't fail since that would we got a libfunc which is not defined in the program + let libfunc_idx = libfunc_indexes.get(&invocation.libfunc_id).unwrap(); + + crate::metadata::libfunc_counter::libfunc_counter_runtime::count_libfunc( + context, + module, + block, + location, + metadata, + *libfunc_idx, + )?; + } + libfunc.build( context, registry, diff --git a/src/executor/aot.rs b/src/executor/aot.rs index 56ee1b2b1..113810ee0 100644 --- a/src/executor/aot.rs +++ b/src/executor/aot.rs @@ -63,6 +63,9 @@ impl AotNativeExecutor { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); + #[cfg(feature = "with-libfunc-counter")] + crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); + executor } diff --git a/src/executor/contract.rs b/src/executor/contract.rs index 833a4d24a..aeb059d50 100644 --- a/src/executor/contract.rs +++ b/src/executor/contract.rs @@ -333,6 +333,9 @@ impl AotContractExecutor { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); + #[cfg(feature = "with-libfunc-counter")] + crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); + Ok(Some(executor)) } diff --git a/src/executor/jit.rs b/src/executor/jit.rs index f7cbe58c7..af38f8d36 100644 --- a/src/executor/jit.rs +++ b/src/executor/jit.rs @@ -74,6 +74,9 @@ impl<'m> JitNativeExecutor<'m> { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); + #[cfg(feature = "with-libfunc-counter")] + crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); + Ok(executor) } diff --git a/src/metadata.rs b/src/metadata.rs index e33656b0c..01fe487f4 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -21,6 +21,7 @@ pub mod dup_overrides; pub mod enum_snapshot_variants; pub mod felt252_dict; pub mod gas; +pub mod libfunc_counter; pub mod profiler; pub mod realloc_bindings; pub mod runtime_bindings; diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs new file mode 100644 index 000000000..48e8e37ba --- /dev/null +++ b/src/metadata/libfunc_counter.rs @@ -0,0 +1,282 @@ +#![cfg(feature = "with-libfunc-counter")] + +use std::{collections::HashSet, os::raw::c_void, ptr}; + +use melior::{ + dialect::{llvm, memref, ods}, + ir::{ + attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, + operation::OperationBuilder, + r#type::{IntegerType, MemRefType}, + Attribute, Block, BlockLike, Location, Module, Region, Value, + }, + Context, +}; + +use crate::{ + error::{Error, Result}, + metadata::libfunc_counter::libfunc_counter_runtime::CounterImpl, + utils::BlockExt, +}; + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub enum LibfuncCounterBinding { + IncCounter, + CounterId, +} + +impl LibfuncCounterBinding { + pub const fn symbol(self) -> &'static str { + match self { + LibfuncCounterBinding::IncCounter => "cairo_native__inc_counter__push_stmt", + LibfuncCounterBinding::CounterId => "cairo_native__counter__profile_id", + } + } + + const fn function_ptr(self) -> *const () { + match self { + LibfuncCounterBinding::IncCounter => CounterImpl::count_libfunc as *const (), + LibfuncCounterBinding::CounterId => ptr::null(), + } + } +} + +#[derive(Clone, Default)] +pub struct LibfuncCounterMeta { + active_map: HashSet, +} + +impl<'c, 'a> LibfuncCounterMeta { + pub fn new() -> Self { + Self { + active_map: HashSet::new(), + } + } + + /// Register the global for the given binding, if not yet registered, and return + /// a pointer to the stored value. + /// + /// For the function to be available, `setup_runtime` must be called before running the module + pub fn build_function( + &mut self, + context: &'c Context, + module: &Module, + block: &'a Block<'c>, + location: Location<'c>, + binding: LibfuncCounterBinding, + ) -> Result> { + if self.active_map.insert(binding) { + module.body().append_operation( + ods::llvm::mlir_global( + context, + Region::new(), + TypeAttribute::new(llvm::r#type::pointer(context, 0)), + StringAttribute::new(context, binding.symbol()), + Attribute::parse(context, "#llvm.linkage") + .ok_or(Error::ParseAttributeError)?, + location, + ) + .into(), + ); + } + + let global_address = block.append_op_result( + ods::llvm::mlir_addressof( + context, + llvm::r#type::pointer(context, 0), + FlatSymbolRefAttribute::new(context, binding.symbol()), + location, + ) + .into(), + )?; + + block.load( + context, + location, + global_address, + llvm::r#type::pointer(context, 0), + ) + } + + pub fn build_counter_id( + &mut self, + context: &'c Context, + module: &Module, + block: &'a Block<'c>, + location: Location<'c>, + ) -> Result> { + if self.active_map.insert(LibfuncCounterBinding::CounterId) { + module.body().append_operation(memref::global( + context, + LibfuncCounterBinding::CounterId.symbol(), + None, + MemRefType::new(IntegerType::new(context, 64).into(), &[], None, None), + None, + false, + None, + location, + )); + } + + let libfunc_counter_id_ptr = block + .append_op_result(memref::get_global( + context, + LibfuncCounterBinding::CounterId.symbol(), + MemRefType::new(IntegerType::new(context, 64).into(), &[], None, None), + location, + )) + .unwrap(); + + block.append_op_result(memref::load(libfunc_counter_id_ptr, &[], location)) + } + + pub fn count_libfunc( + &mut self, + context: &Context, + module: &Module, + block: &'a Block<'c>, + location: Location, + libfunc_idx: usize, + ) -> Result<()> { + let counter_id = self.build_counter_id(context, module, block, location)?; + let libfunc_idx = block.const_int(context, location, libfunc_idx, 32)?; + + let function_ptr = self.build_function( + context, + module, + block, + location, + LibfuncCounterBinding::IncCounter, + )?; + + block.append_operation( + OperationBuilder::new("llvm.call", location) + .add_operands(&[function_ptr]) + .add_operands(&[counter_id, libfunc_idx]) + .build()?, + ); + + Ok(()) + } + + // pub fn build_array_counter( + // context: &'c Context, + // module: &Module, + // block: &'a Block<'c>, + // location: Location<'c>, + // libfunc_count: u32, + // ) { + // let array_ty = llvm::r#type::array(IntegerType::new(context, 32), libfunc_count); + // let (layout, align) = layout_repeat(get_integer_layout(32), libfunc_count)?; + // let array_counter_ptr = block.alloca1(context, location, array_ty, align)?; + // } + + // pub fn count_libfunc( + // &self, + // context: &Context, + // registry: &Program, + // module: &Module, + // block: &'a Block<'c>, + // location: Location, + // metadata: &mut MetadataStorage, + // libfunc_id: ConcreteLibfuncId, + // ) -> Result<()> { + // let u32_ty = IntegerType::new(context, 32); + // let array_ty = llvm::r#type::array(u32_ty, libfunc_count); + // let k1 = block.const_int(context, location, 1, 32)?; + + // let array_counter_ptr = block.load( + // context, + // location, + // libfunc_counter.array_counter_ptr, + // array_ty, + // )?; + // let value_counter = block.gep( + // context, + // location, + // array_counter_ptr, + // GepIndex::Const(libfunc_id.id), + // u32_ty, + // )?; + // let value_incremented = block.addi(value_counter, k1, location)?; + + // block.insert_value( + // context, + // location, + // array_counter_ptr, + // value_incremented, + // libfunc_id.id, + // )?; + + // Ok(()) + // } +} + +pub fn setup_runtime(find_symbol_ptr: impl Fn(&str) -> Option<*mut c_void>) { + let bindings = &[LibfuncCounterBinding::IncCounter]; + + for binding in bindings { + if let Some(global) = find_symbol_ptr(binding.symbol()) { + let global = global.cast::<*const ()>(); + unsafe { *global = binding.function_ptr() }; + } + } +} + +pub mod libfunc_counter_runtime { + use std::{ + collections::HashMap, + sync::{LazyLock, Mutex}, + }; + + use melior::{ + ir::{BlockRef, Location, Module}, + Context, + }; + + use crate::{ + error::Result, + metadata::{libfunc_counter::LibfuncCounterMeta, MetadataStorage}, + }; + + pub static LIBFUNC_COUNTER: LazyLock>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + + pub fn count_libfunc( + context: &Context, + module: &Module, + block: &BlockRef, + location: Location, + metadata: &mut MetadataStorage, + libfunc_idx: usize, + ) -> Result<()> { + let libfunc_counter = metadata.get_or_insert_with(LibfuncCounterMeta::default); + + libfunc_counter.count_libfunc(context, module, block, location, libfunc_idx) + } + + #[derive(Default, Debug)] + pub struct CounterImpl { + pub array_counter: Vec, + } + + impl CounterImpl { + pub fn new(libfunc_amount: usize) -> Self { + let array_counter = vec![0u32; libfunc_amount]; + + Self { + array_counter, + } + } + + pub extern "C" fn count_libfunc(counter_id: u64, libfunc_idx: u32) { + let index = libfunc_idx as usize; + let mut libfunc_counter_map = LIBFUNC_COUNTER.lock().unwrap(); + let libfunc_counter = libfunc_counter_map.get_mut(&counter_id).unwrap(); + + let counter = libfunc_counter.array_counter.get_mut(index).unwrap(); + + *counter += 1; + } + } +} From b93ae5b9270c0c907b17aeee701be5202fac36a5 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 1 Jul 2025 17:48:57 -0300 Subject: [PATCH 05/21] implement with mlir array --- src/bin/cairo-native-run.rs | 16 +-- src/compiler.rs | 11 +- src/metadata/libfunc_counter.rs | 242 ++++++++++++++++++++------------ 3 files changed, 160 insertions(+), 109 deletions(-) diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index e73479abc..7eb7f5046 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -15,7 +15,7 @@ use cairo_native::{ starknet_stub::StubSyscallHandler, }; use clap::{Parser, ValueEnum}; -#[cfg(any(feature = "with-libfunc-profiling", feature = "with-libfunc-counter"))] +#[cfg(any(feature = "with-libfunc-profiling"))] use std::collections::HashMap; use std::path::PathBuf; use tracing_subscriber::{EnvFilter, FmtSubscriber}; @@ -213,18 +213,6 @@ fn main() -> anyhow::Result<()> { .insert(0, ProfilerImpl::new()); } - #[cfg(feature = "with-libfunc-counter")] - { - use cairo_native::metadata::libfunc_counter::libfunc_counter_runtime::{ - CounterImpl, LIBFUNC_COUNTER, - }; - - LIBFUNC_COUNTER.lock().unwrap().insert( - 0, - CounterImpl::new(sierra_program.libfunc_declarations.len()), - ); - } - let gas_metadata = GasMetadata::new(&sierra_program, Some(MetadataComputationConfig::default())).unwrap(); @@ -333,7 +321,6 @@ fn main() -> anyhow::Result<()> { let libfunc_counter = counters.values().next().unwrap(); let libfunc_counts = libfunc_counter - .array_counter .iter() .enumerate() .map(|(i, count)| { @@ -343,7 +330,6 @@ fn main() -> anyhow::Result<()> { (debug_name, *count) }) .collect::>(); - dbg!(&libfunc_counts); serde_json::to_writer_pretty( std::fs::File::create(libfunc_counter_output).unwrap(), &libfunc_counts, diff --git a/src/compiler.rs b/src/compiler.rs index 776aaa25d..f7793c173 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -661,10 +661,11 @@ fn compile_func( crate::metadata::libfunc_counter::libfunc_counter_runtime::count_libfunc( context, module, - block, + &block, location, metadata, *libfunc_idx, + libfunc_indexes.len() as u32, )?; } @@ -952,6 +953,14 @@ fn compile_func( }, )?; + #[cfg(feature = "with-libfunc-counter")] + { + use crate::metadata::libfunc_counter::LibfuncCounterMeta; + + let libfunc_counter = metadata.get_mut::().unwrap(); + libfunc_counter.store_array_counter(context, module, &entry_block, Location::unknown(context), libfunc_indexes.len() as u32)?; + } + // Load arguments and jump to the entry block. { let mut arg_values = Vec::with_capacity(function.signature.param_types.len()); diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 48e8e37ba..0583c68bc 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -15,28 +15,34 @@ use melior::{ use crate::{ error::{Error, Result}, - metadata::libfunc_counter::libfunc_counter_runtime::CounterImpl, - utils::BlockExt, + utils::{BlockExt, GepIndex}, }; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum LibfuncCounterBinding { - IncCounter, + StoreArrayCounter, CounterId, + ArrayCounter, } impl LibfuncCounterBinding { pub const fn symbol(self) -> &'static str { match self { - LibfuncCounterBinding::IncCounter => "cairo_native__inc_counter__push_stmt", + LibfuncCounterBinding::StoreArrayCounter => { + "cairo_native__store_array_counter__push_stmt" + } LibfuncCounterBinding::CounterId => "cairo_native__counter__profile_id", + LibfuncCounterBinding::ArrayCounter => "cairo_native__array_counter__profile_id", } } const fn function_ptr(self) -> *const () { match self { - LibfuncCounterBinding::IncCounter => CounterImpl::count_libfunc as *const (), + LibfuncCounterBinding::StoreArrayCounter => { + libfunc_counter_runtime::store_array_counter as *const () + } LibfuncCounterBinding::CounterId => ptr::null(), + LibfuncCounterBinding::ArrayCounter => ptr::null(), } } } @@ -46,7 +52,7 @@ pub struct LibfuncCounterMeta { active_map: HashSet, } -impl<'c, 'a> LibfuncCounterMeta { +impl LibfuncCounterMeta { pub fn new() -> Self { Self { active_map: HashSet::new(), @@ -57,7 +63,7 @@ impl<'c, 'a> LibfuncCounterMeta { /// a pointer to the stored value. /// /// For the function to be available, `setup_runtime` must be called before running the module - pub fn build_function( + pub fn build_function<'c, 'a>( &mut self, context: &'c Context, module: &Module, @@ -98,7 +104,7 @@ impl<'c, 'a> LibfuncCounterMeta { ) } - pub fn build_counter_id( + pub fn build_counter_id<'c, 'a>( &mut self, context: &'c Context, module: &Module, @@ -130,90 +136,146 @@ impl<'c, 'a> LibfuncCounterMeta { block.append_op_result(memref::load(libfunc_counter_id_ptr, &[], location)) } - pub fn count_libfunc( + pub fn store_array_counter<'c, 'a>( &mut self, context: &Context, module: &Module, block: &'a Block<'c>, location: Location, - libfunc_idx: usize, + libfunc_amount: u32 ) -> Result<()> { - let counter_id = self.build_counter_id(context, module, block, location)?; - let libfunc_idx = block.const_int(context, location, libfunc_idx, 32)?; + let array_ty = llvm::r#type::array(IntegerType::new(context, 32).into(), libfunc_amount); + let counter_id = self.build_counter_id(context, module, &block, location)?; let function_ptr = self.build_function( context, module, - block, + &block, location, - LibfuncCounterBinding::IncCounter, + LibfuncCounterBinding::StoreArrayCounter, + )?; + let lifuncs_amount = block.const_int(context, location, libfunc_amount, 32)?; + // by this time, the array counter should be initialized + let global_address = block.append_op_result( + ods::llvm::mlir_addressof( + context, + llvm::r#type::pointer(context, 0), + FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), + location, + ) + .into(), )?; + let array_counter_ptr = block.load(context, location, global_address, array_ty)?; + block.append_operation( OperationBuilder::new("llvm.call", location) .add_operands(&[function_ptr]) - .add_operands(&[counter_id, libfunc_idx]) + .add_operands(&[counter_id, array_counter_ptr, lifuncs_amount]) .build()?, ); Ok(()) } - // pub fn build_array_counter( - // context: &'c Context, - // module: &Module, - // block: &'a Block<'c>, - // location: Location<'c>, - // libfunc_count: u32, - // ) { - // let array_ty = llvm::r#type::array(IntegerType::new(context, 32), libfunc_count); - // let (layout, align) = layout_repeat(get_integer_layout(32), libfunc_count)?; - // let array_counter_ptr = block.alloca1(context, location, array_ty, align)?; - // } - - // pub fn count_libfunc( - // &self, - // context: &Context, - // registry: &Program, - // module: &Module, - // block: &'a Block<'c>, - // location: Location, - // metadata: &mut MetadataStorage, - // libfunc_id: ConcreteLibfuncId, - // ) -> Result<()> { - // let u32_ty = IntegerType::new(context, 32); - // let array_ty = llvm::r#type::array(u32_ty, libfunc_count); - // let k1 = block.const_int(context, location, 1, 32)?; - - // let array_counter_ptr = block.load( - // context, - // location, - // libfunc_counter.array_counter_ptr, - // array_ty, - // )?; - // let value_counter = block.gep( - // context, - // location, - // array_counter_ptr, - // GepIndex::Const(libfunc_id.id), - // u32_ty, - // )?; - // let value_incremented = block.addi(value_counter, k1, location)?; - - // block.insert_value( - // context, - // location, - // array_counter_ptr, - // value_incremented, - // libfunc_id.id, - // )?; - - // Ok(()) - // } + fn build_array_counter<'c, 'a>( + &mut self, + context: &'c Context, + module: &Module, + block: &'a Block<'c>, + location: Location<'c>, + libfunc_amount: u32, + ) -> Result<()> { + let array_ty = llvm::r#type::array(IntegerType::new(context, 32).into(), libfunc_amount); + let k0 = block.const_int(context, location, 0, 32)?; + + module.body().append_operation( + ods::llvm::mlir_global( + context, + Region::new(), + TypeAttribute::new(array_ty), + StringAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), + Attribute::parse(context, "#llvm.linkage") + .ok_or(Error::ParseAttributeError)?, + location, + ) + .into(), + ); + + let global_address = block.append_op_result( + ods::llvm::mlir_addressof( + context, + llvm::r#type::pointer(context, 0), + FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), + location, + ) + .into(), + )?; + + let array_counter_ptr = block.load(context, location, global_address, array_ty)?; + + block.insert_values( + context, + location, + array_counter_ptr, + &vec![k0; libfunc_amount as usize], + )?; + + Ok(()) + } + + pub fn count_libfunc<'c, 'a>( + &mut self, + context: &Context, + module: &Module, + block: &'a Block<'c>, + location: Location, + libfunc_idx: usize, + libfuncs_amount: u32, + ) -> Result<()> { + if self.active_map.insert(LibfuncCounterBinding::ArrayCounter) { + self.build_array_counter(context, module, block, location, libfuncs_amount)?; + } + + let u32_ty = IntegerType::new(context, 32).into(); + let array_ty = llvm::r#type::array(u32_ty, libfuncs_amount); + let k1 = block.const_int(context, location, 1, 32)?; + + let global_address = block.append_op_result( + ods::llvm::mlir_addressof( + context, + llvm::r#type::pointer(context, 0), + FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), + location, + ) + .into(), + )?; + + let array_counter_ptr = block.load(context, location, global_address, array_ty)?; + + let value_counter = block.gep( + context, + location, + array_counter_ptr, + &[GepIndex::Const(libfunc_idx as i32)], + u32_ty, + )?; + let value_incremented = block.addi(value_counter, k1, location)?; + + block.insert_value( + context, + location, + array_counter_ptr, + value_incremented, + libfunc_idx, + )?; + + Ok(()) + } } pub fn setup_runtime(find_symbol_ptr: impl Fn(&str) -> Option<*mut c_void>) { - let bindings = &[LibfuncCounterBinding::IncCounter]; + let bindings = &[LibfuncCounterBinding::StoreArrayCounter]; for binding in bindings { if let Some(global) = find_symbol_ptr(binding.symbol()) { @@ -229,8 +291,9 @@ pub mod libfunc_counter_runtime { sync::{LazyLock, Mutex}, }; + use itertools::Itertools; use melior::{ - ir::{BlockRef, Location, Module}, + ir::{Block, Location, Module}, Context, }; @@ -239,44 +302,37 @@ pub mod libfunc_counter_runtime { metadata::{libfunc_counter::LibfuncCounterMeta, MetadataStorage}, }; - pub static LIBFUNC_COUNTER: LazyLock>> = + pub static LIBFUNC_COUNTER: LazyLock>>> = LazyLock::new(|| Mutex::new(HashMap::new())); pub fn count_libfunc( context: &Context, module: &Module, - block: &BlockRef, + block: &Block, location: Location, metadata: &mut MetadataStorage, libfunc_idx: usize, + libfuncs_amount: u32, ) -> Result<()> { let libfunc_counter = metadata.get_or_insert_with(LibfuncCounterMeta::default); - libfunc_counter.count_libfunc(context, module, block, location, libfunc_idx) - } - - #[derive(Default, Debug)] - pub struct CounterImpl { - pub array_counter: Vec, + libfunc_counter.count_libfunc( + context, + module, + block, + location, + libfunc_idx, + libfuncs_amount, + ) } - impl CounterImpl { - pub fn new(libfunc_amount: usize) -> Self { - let array_counter = vec![0u32; libfunc_amount]; - - Self { - array_counter, - } - } - - pub extern "C" fn count_libfunc(counter_id: u64, libfunc_idx: u32) { - let index = libfunc_idx as usize; - let mut libfunc_counter_map = LIBFUNC_COUNTER.lock().unwrap(); - let libfunc_counter = libfunc_counter_map.get_mut(&counter_id).unwrap(); + pub unsafe extern "C" fn store_array_counter( + counter_id: u64, + array_counter: &[u32], + libfuncs_amount: u32, + ) { + let mut libfunc_counter = LIBFUNC_COUNTER.lock().unwrap(); - let counter = libfunc_counter.array_counter.get_mut(index).unwrap(); - - *counter += 1; - } + libfunc_counter.insert(counter_id, array_counter.to_vec()); } } From 951ac4c7892b76cdc1e51fc2a1f11c1e5c39164a Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 2 Jul 2025 11:09:35 -0300 Subject: [PATCH 06/21] fix implementation with mlir --- src/compiler.rs | 28 +++++++++++++++------ src/metadata/libfunc_counter.rs | 44 +++++++++++---------------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index f7793c173..a2db84485 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -953,14 +953,6 @@ fn compile_func( }, )?; - #[cfg(feature = "with-libfunc-counter")] - { - use crate::metadata::libfunc_counter::LibfuncCounterMeta; - - let libfunc_counter = metadata.get_mut::().unwrap(); - libfunc_counter.store_array_counter(context, module, &entry_block, Location::unknown(context), libfunc_indexes.len() as u32)?; - } - // Load arguments and jump to the entry block. { let mut arg_values = Vec::with_capacity(function.signature.param_types.len()); @@ -1070,6 +1062,10 @@ fn compile_func( sierra_stmt_start_offset + function.entry_point.0, 0, ), + #[cfg(feature = "with-libfunc-counter")] + libfunc_indexes, + #[cfg(feature = "with-libfunc-counter")] + metadata, )?; tracing::debug!("Done generating function {}.", function.id); @@ -1451,6 +1447,8 @@ fn generate_entry_point_wrapper<'c>( arg_types: &[(Type<'c>, Location<'c>)], ret_types: &[Type<'c>], location: Location<'c>, + #[cfg(feature = "with-libfunc-counter")] libfunc_indexes: &HashMap, + #[cfg(feature = "with-libfunc-counter")] metadata: &mut MetadataStorage, ) -> Result<(), Error> { let region = Region::new(); let block = region.append_block(Block::new(arg_types)); @@ -1483,6 +1481,20 @@ fn generate_entry_point_wrapper<'c>( returns.push(block.extract_value(context, location, result, *ty, i)?); } + #[cfg(feature = "with-libfunc-counter")] + { + use crate::metadata::libfunc_counter::LibfuncCounterMeta; + + let libfunc_counter = metadata.get_mut::().unwrap(); + libfunc_counter.store_array_counter( + context, + module, + &block, + location, + libfunc_indexes.len() as u32, + )?; + } + block.append_operation(func::r#return(&returns, location)); module.body().append_operation(func::func( diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 0583c68bc..0142f3bb5 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -15,7 +15,7 @@ use melior::{ use crate::{ error::{Error, Result}, - utils::{BlockExt, GepIndex}, + utils::BlockExt, }; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] @@ -142,7 +142,7 @@ impl LibfuncCounterMeta { module: &Module, block: &'a Block<'c>, location: Location, - libfunc_amount: u32 + libfunc_amount: u32, ) -> Result<()> { let array_ty = llvm::r#type::array(IntegerType::new(context, 32).into(), libfunc_amount); @@ -166,12 +166,10 @@ impl LibfuncCounterMeta { .into(), )?; - let array_counter_ptr = block.load(context, location, global_address, array_ty)?; - block.append_operation( OperationBuilder::new("llvm.call", location) .add_operands(&[function_ptr]) - .add_operands(&[counter_id, array_counter_ptr, lifuncs_amount]) + .add_operands(&[counter_id, global_address, lifuncs_amount]) .build()?, ); @@ -212,15 +210,6 @@ impl LibfuncCounterMeta { .into(), )?; - let array_counter_ptr = block.load(context, location, global_address, array_ty)?; - - block.insert_values( - context, - location, - array_counter_ptr, - &vec![k0; libfunc_amount as usize], - )?; - Ok(()) } @@ -241,7 +230,7 @@ impl LibfuncCounterMeta { let array_ty = llvm::r#type::array(u32_ty, libfuncs_amount); let k1 = block.const_int(context, location, 1, 32)?; - let global_address = block.append_op_result( + let array_counter_ptr = block.append_op_result( ods::llvm::mlir_addressof( context, llvm::r#type::pointer(context, 0), @@ -251,25 +240,21 @@ impl LibfuncCounterMeta { .into(), )?; - let array_counter_ptr = block.load(context, location, global_address, array_ty)?; + let array_counter = block.load(context, location, array_counter_ptr, array_ty)?; - let value_counter = block.gep( - context, - location, - array_counter_ptr, - &[GepIndex::Const(libfunc_idx as i32)], - u32_ty, - )?; + let value_counter = block.extract_value(context, location, array_counter, u32_ty, libfunc_idx)?; let value_incremented = block.addi(value_counter, k1, location)?; - - block.insert_value( + + let array_counter = block.insert_value( context, location, - array_counter_ptr, + array_counter, value_incremented, libfunc_idx, )?; + block.store(context, location, array_counter_ptr, array_counter)?; + Ok(()) } } @@ -328,11 +313,12 @@ pub mod libfunc_counter_runtime { pub unsafe extern "C" fn store_array_counter( counter_id: u64, - array_counter: &[u32], + array_counter: *const u32, libfuncs_amount: u32, ) { let mut libfunc_counter = LIBFUNC_COUNTER.lock().unwrap(); - - libfunc_counter.insert(counter_id, array_counter.to_vec()); + let vec = (0..libfuncs_amount).map(|i| dbg!(*array_counter.add(i as usize))).collect_vec(); + + libfunc_counter.insert(counter_id, vec); } } From 5f445309deb670fc3bb2e570fdcc1507a6db2f32 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 2 Jul 2025 12:44:43 -0300 Subject: [PATCH 07/21] add documentation --- src/compiler.rs | 16 +-------- src/metadata/libfunc_counter.rs | 62 ++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index a2db84485..386837015 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -527,20 +527,6 @@ fn compile_func( 0, ); - #[cfg(feature = "with-debug-utils")] - { - // If this env var exists and is a valid statement, insert a debug trap before the libfunc call. - // Only on when using with-debug-utils feature. - if let Ok(x) = std::env::var("NATIVE_DEBUG_TRAP_AT_STMT") { - if x.eq_ignore_ascii_case(&statement_idx.0.to_string()) { - block.append_operation( - melior::dialect::ods::llvm::intr_debugtrap(context, location) - .into(), - ); - } - } - } - let libfunc_name = if invocation.libfunc_id.debug_name.is_some() { format!("{}(stmt_idx={})", invocation.libfunc_id, statement_idx) } else { @@ -661,7 +647,7 @@ fn compile_func( crate::metadata::libfunc_counter::libfunc_counter_runtime::count_libfunc( context, module, - &block, + block, location, metadata, *libfunc_idx, diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 0142f3bb5..ff3656571 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -1,5 +1,25 @@ #![cfg(feature = "with-libfunc-counter")] - +//! The libfunc counter feature is used to generate information counting how many time a libfunc has been called. +//! +//! When this feature is used, the compiler will call the important methods: +//! +//! 1. `count_libfunc`: called before every libfunc execution. +//! +//! 2. `build_array_counter`: called only once to build the array of counters (one for each libfuncs). The order of +//! is based on the libfuncs' declaration order. +//! +//! 3. `store_array_counter`: called before finishing each entrypoint execution. It transforms the MLIR array into a +//! Rust vector which is then stored in `LIBFUNC_COUNTER`, a static variable that registers the array of counters by +//! execution, along with its `counter_id` (which is relative to the execution). +//! +//! In the context of Starknet contracts, we need to add support for building +//! the arrays of counters for multiple executions. To do so, we need one important element, which must be set before every contract +//! execution: +//! +//! A counter to track the ID of the current array of counter, which gets updated every time we switch to another +//! contract. Since a contract can call other contracts, we need a way of restoring the counter after every execution. +//! +//! See `cairo-native-run` for an example on how to do it. use std::{collections::HashSet, os::raw::c_void, ptr}; use melior::{ @@ -104,7 +124,7 @@ impl LibfuncCounterMeta { ) } - pub fn build_counter_id<'c, 'a>( + fn build_counter_id<'c, 'a>( &mut self, context: &'c Context, module: &Module, @@ -136,6 +156,8 @@ impl LibfuncCounterMeta { block.append_op_result(memref::load(libfunc_counter_id_ptr, &[], location)) } + /// Indexes the array of counters and increments + /// the one relative to the given libfunc index pub fn store_array_counter<'c, 'a>( &mut self, context: &Context, @@ -144,13 +166,11 @@ impl LibfuncCounterMeta { location: Location, libfunc_amount: u32, ) -> Result<()> { - let array_ty = llvm::r#type::array(IntegerType::new(context, 32).into(), libfunc_amount); - - let counter_id = self.build_counter_id(context, module, &block, location)?; + let counter_id = self.build_counter_id(context, module, block, location)?; let function_ptr = self.build_function( context, module, - &block, + block, location, LibfuncCounterBinding::StoreArrayCounter, )?; @@ -176,16 +196,15 @@ impl LibfuncCounterMeta { Ok(()) } - fn build_array_counter<'c, 'a>( + /// Build the array of counters + fn build_array_counter<'c>( &mut self, context: &'c Context, module: &Module, - block: &'a Block<'c>, location: Location<'c>, libfunc_amount: u32, ) -> Result<()> { let array_ty = llvm::r#type::array(IntegerType::new(context, 32).into(), libfunc_amount); - let k0 = block.const_int(context, location, 0, 32)?; module.body().append_operation( ods::llvm::mlir_global( @@ -200,16 +219,6 @@ impl LibfuncCounterMeta { .into(), ); - let global_address = block.append_op_result( - ods::llvm::mlir_addressof( - context, - llvm::r#type::pointer(context, 0), - FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), - location, - ) - .into(), - )?; - Ok(()) } @@ -223,7 +232,7 @@ impl LibfuncCounterMeta { libfuncs_amount: u32, ) -> Result<()> { if self.active_map.insert(LibfuncCounterBinding::ArrayCounter) { - self.build_array_counter(context, module, block, location, libfuncs_amount)?; + self.build_array_counter(context, module, location, libfuncs_amount)?; } let u32_ty = IntegerType::new(context, 32).into(); @@ -242,9 +251,10 @@ impl LibfuncCounterMeta { let array_counter = block.load(context, location, array_counter_ptr, array_ty)?; - let value_counter = block.extract_value(context, location, array_counter, u32_ty, libfunc_idx)?; + let value_counter = + block.extract_value(context, location, array_counter, u32_ty, libfunc_idx)?; let value_incremented = block.addi(value_counter, k1, location)?; - + let array_counter = block.insert_value( context, location, @@ -287,9 +297,11 @@ pub mod libfunc_counter_runtime { metadata::{libfunc_counter::LibfuncCounterMeta, MetadataStorage}, }; + /// Contains an array of vector for each execution completed pub static LIBFUNC_COUNTER: LazyLock>>> = LazyLock::new(|| Mutex::new(HashMap::new())); + /// Increase the libfunc's counter given its index pub fn count_libfunc( context: &Context, module: &Module, @@ -317,8 +329,10 @@ pub mod libfunc_counter_runtime { libfuncs_amount: u32, ) { let mut libfunc_counter = LIBFUNC_COUNTER.lock().unwrap(); - let vec = (0..libfuncs_amount).map(|i| dbg!(*array_counter.add(i as usize))).collect_vec(); - + let vec = (0..libfuncs_amount) + .map(|i| *array_counter.add(i as usize)) + .collect_vec(); + libfunc_counter.insert(counter_id, vec); } } From f890e42e02a5eb7ee5ace527f9f609384e7688ae Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 2 Jul 2025 12:51:25 -0300 Subject: [PATCH 08/21] revert change --- src/compiler.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/compiler.rs b/src/compiler.rs index 386837015..23ba3c667 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -527,6 +527,20 @@ fn compile_func( 0, ); + #[cfg(feature = "with-debug-utils")] + { + // If this env var exists and is a valid statement, insert a debug trap before the libfunc call. + // Only on when using with-debug-utils feature. + if let Ok(x) = std::env::var("NATIVE_DEBUG_TRAP_AT_STMT") { + if x.eq_ignore_ascii_case(&statement_idx.0.to_string()) { + block.append_operation( + melior::dialect::ods::llvm::intr_debugtrap(context, location) + .into(), + ); + } + } + } + let libfunc_name = if invocation.libfunc_id.debug_name.is_some() { format!("{}(stmt_idx={})", invocation.libfunc_id, statement_idx) } else { From 81511e16b7e1a6de2413c759765631f2469c7207 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 2 Jul 2025 13:04:34 -0300 Subject: [PATCH 09/21] clippy --- src/metadata/libfunc_counter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index ff3656571..e8a747491 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -158,11 +158,11 @@ impl LibfuncCounterMeta { /// Indexes the array of counters and increments /// the one relative to the given libfunc index - pub fn store_array_counter<'c, 'a>( + pub fn store_array_counter( &mut self, context: &Context, module: &Module, - block: &'a Block<'c>, + block: &Block<'_>, location: Location, libfunc_amount: u32, ) -> Result<()> { @@ -222,11 +222,11 @@ impl LibfuncCounterMeta { Ok(()) } - pub fn count_libfunc<'c, 'a>( + pub fn count_libfunc( &mut self, context: &Context, module: &Module, - block: &'a Block<'c>, + block: &Block<'_>, location: Location, libfunc_idx: usize, libfuncs_amount: u32, From 06aef997d8b606cb18108405642013cc2988034e Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 3 Jul 2025 10:47:27 -0300 Subject: [PATCH 10/21] fix some docs --- src/metadata/libfunc_counter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index e8a747491..1d50e21ec 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -1,7 +1,7 @@ #![cfg(feature = "with-libfunc-counter")] //! The libfunc counter feature is used to generate information counting how many time a libfunc has been called. //! -//! When this feature is used, the compiler will call the important methods: +//! When this feature is used, the compiler will call three important methods: //! //! 1. `count_libfunc`: called before every libfunc execution. //! @@ -156,8 +156,8 @@ impl LibfuncCounterMeta { block.append_op_result(memref::load(libfunc_counter_id_ptr, &[], location)) } - /// Indexes the array of counters and increments - /// the one relative to the given libfunc index + /// Indexes the array of counters and increments the counter relative + /// to the given libfunc index pub fn store_array_counter( &mut self, context: &Context, From f930c1faaf441a8a33ea72288b96a99456afaa4b Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 3 Jul 2025 15:08:07 -0300 Subject: [PATCH 11/21] update comment --- src/compiler.rs | 4 +++- src/metadata/libfunc_counter.rs | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 23ba3c667..1b9d10395 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -655,7 +655,9 @@ fn compile_func( #[cfg(feature = "with-libfunc-counter")] { - // Can't fail since that would we got a libfunc which is not defined in the program + // Can't fail. If we had a key from the invocation statement that is not included in this hashmap, + // that would mean that there's an error in the sierra program since we got to invoke a libfunc that has + // not been declared. let libfunc_idx = libfunc_indexes.get(&invocation.libfunc_id).unwrap(); crate::metadata::libfunc_counter::libfunc_counter_runtime::count_libfunc( diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 1d50e21ec..7bbd89b05 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -49,10 +49,10 @@ impl LibfuncCounterBinding { pub const fn symbol(self) -> &'static str { match self { LibfuncCounterBinding::StoreArrayCounter => { - "cairo_native__store_array_counter__push_stmt" + "cairo_native__store_array_counter" } - LibfuncCounterBinding::CounterId => "cairo_native__counter__profile_id", - LibfuncCounterBinding::ArrayCounter => "cairo_native__array_counter__profile_id", + LibfuncCounterBinding::CounterId => "cairo_native__counter_id", + LibfuncCounterBinding::ArrayCounter => "cairo_native__array_counter", } } From a3bb89b95d25a8e00213112ddaf6527f2afeefdb Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 3 Jul 2025 15:14:17 -0300 Subject: [PATCH 12/21] format --- src/compiler.rs | 4 ++-- src/metadata/libfunc_counter.rs | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 1b9d10395..14213cdb3 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -655,8 +655,8 @@ fn compile_func( #[cfg(feature = "with-libfunc-counter")] { - // Can't fail. If we had a key from the invocation statement that is not included in this hashmap, - // that would mean that there's an error in the sierra program since we got to invoke a libfunc that has + // Can't fail. If we had a key from the invocation statement that is not included in this hashmap, + // that would mean that there's an error in the sierra program since we got to invoke a libfunc that has // not been declared. let libfunc_idx = libfunc_indexes.get(&invocation.libfunc_id).unwrap(); diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 7bbd89b05..ff46e6dfd 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -48,9 +48,7 @@ pub enum LibfuncCounterBinding { impl LibfuncCounterBinding { pub const fn symbol(self) -> &'static str { match self { - LibfuncCounterBinding::StoreArrayCounter => { - "cairo_native__store_array_counter" - } + LibfuncCounterBinding::StoreArrayCounter => "cairo_native__store_array_counter", LibfuncCounterBinding::CounterId => "cairo_native__counter_id", LibfuncCounterBinding::ArrayCounter => "cairo_native__array_counter", } From 2061d85b3f9b2d0f6ec390144e6ff17fd5fbafb0 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 4 Jul 2025 18:28:35 -0300 Subject: [PATCH 13/21] change to dynamic arrays (not working yet) --- src/bin/cairo-native-run.rs | 40 +++++++++-- src/metadata/libfunc_counter.rs | 123 +++++++++++++++++++++----------- 2 files changed, 117 insertions(+), 46 deletions(-) diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index 259a195bc..c45cd9143 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -137,12 +137,28 @@ fn main() -> anyhow::Result<()> { } Box::new(move |function_id, args, gas, syscall_handler| { - executor.invoke_dynamic_with_syscall_handler( + let result = executor.invoke_dynamic_with_syscall_handler( function_id, args, gas, syscall_handler, - ) + ); + + // Deallocate the array of counters + #[cfg(feature = "with-libfunc-counter")] + { + use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; + + if let Some(array_counter_ptr) = + executor.find_symbol_ptr(LibfuncCounterBinding::ArrayCounter.symbol()) + { + // unsafe { + // libc::free(array_counter_ptr); + // } + } + } + + result }) } RunMode::Jit => { @@ -182,12 +198,28 @@ fn main() -> anyhow::Result<()> { } Box::new(move |function_id, args, gas, syscall_handler| { - executor.invoke_dynamic_with_syscall_handler( + let result = executor.invoke_dynamic_with_syscall_handler( function_id, args, gas, syscall_handler, - ) + ); + + // Deallocate the array of counters + #[cfg(feature = "with-libfunc-counter")] + { + use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; + + if let Some(array_counter_ptr) = + executor.find_symbol_ptr(LibfuncCounterBinding::ArrayCounter.symbol()) + { + // unsafe { + // libc::free(*array_counter_ptr); + // } + } + } + + result }) } }; diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index ff46e6dfd..552f9799a 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -35,7 +35,8 @@ use melior::{ use crate::{ error::{Error, Result}, - utils::BlockExt, + metadata::realloc_bindings::ReallocBindingsMeta, + utils::{get_integer_layout, layout_repeat, BlockExt, GepIndex}, }; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] @@ -174,7 +175,7 @@ impl LibfuncCounterMeta { )?; let lifuncs_amount = block.const_int(context, location, libfunc_amount, 32)?; // by this time, the array counter should be initialized - let global_address = block.append_op_result( + let array_counter_ptr_ptr = block.append_op_result( ods::llvm::mlir_addressof( context, llvm::r#type::pointer(context, 0), @@ -183,11 +184,13 @@ impl LibfuncCounterMeta { ) .into(), )?; + let array_counter_ptr = block.load(context, location, array_counter_ptr_ptr, llvm::r#type::pointer(context, 0))?; + block.append_operation( OperationBuilder::new("llvm.call", location) .add_operands(&[function_ptr]) - .add_operands(&[counter_id, global_address, lifuncs_amount]) + .add_operands(&[counter_id, array_counter_ptr, lifuncs_amount]) .build()?, ); @@ -195,29 +198,81 @@ impl LibfuncCounterMeta { } /// Build the array of counters - fn build_array_counter<'c>( + fn get_array_counter<'c, 'a>( &mut self, context: &'c Context, module: &Module, + block: &'a Block<'c>, location: Location<'c>, libfunc_amount: u32, - ) -> Result<()> { - let array_ty = llvm::r#type::array(IntegerType::new(context, 32).into(), libfunc_amount); + ) -> Result> { + if self.active_map.insert(LibfuncCounterBinding::ArrayCounter) { + module.body().append_operation( + ods::llvm::mlir_global( + context, + Region::new(), + TypeAttribute::new(llvm::r#type::pointer(context, 0)), + StringAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), + Attribute::parse(context, "#llvm.linkage") + .ok_or(Error::ParseAttributeError)?, + location, + ) + .into(), + ); + + // Once we created the global pointer to the counters, we need to reallocate it so that it + // can hold as many counters as libfuncs declared + let u32_layout = get_integer_layout(32); + let libfuncs_amount_bytes = layout_repeat(&u32_layout, libfunc_amount as usize)? + .0 + .pad_to_align() + .size(); + let libfuncs_amount_bytes = + block.const_int(context, location, libfuncs_amount_bytes, 64)?; + + let array_counter_ptr_ptr = block.append_op_result( + ods::llvm::mlir_addressof( + context, + llvm::r#type::pointer(context, 0), + FlatSymbolRefAttribute::new( + context, + LibfuncCounterBinding::ArrayCounter.symbol(), + ), + location, + ) + .into(), + )?; - module.body().append_operation( - ods::llvm::mlir_global( + let array_counter_ptr = + block.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?; + + let array_counter_ptr = block.append_op_result(ReallocBindingsMeta::realloc( + context, + array_counter_ptr, + libfuncs_amount_bytes, + location, + )?)?; + + block.store(context, location, array_counter_ptr_ptr, array_counter_ptr)?; + } + + let array_counter_ptr_ptr = block.append_op_result( + ods::llvm::mlir_addressof( context, - Region::new(), - TypeAttribute::new(array_ty), - StringAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), - Attribute::parse(context, "#llvm.linkage") - .ok_or(Error::ParseAttributeError)?, + llvm::r#type::pointer(context, 0), + FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), location, ) .into(), - ); + )?; - Ok(()) + // // return the pointer to array counter + block.load( + context, + location, + array_counter_ptr_ptr, + llvm::r#type::pointer(context, 0), + ) } pub fn count_libfunc( @@ -229,39 +284,23 @@ impl LibfuncCounterMeta { libfunc_idx: usize, libfuncs_amount: u32, ) -> Result<()> { - if self.active_map.insert(LibfuncCounterBinding::ArrayCounter) { - self.build_array_counter(context, module, location, libfuncs_amount)?; - } - let u32_ty = IntegerType::new(context, 32).into(); - let array_ty = llvm::r#type::array(u32_ty, libfuncs_amount); - let k1 = block.const_int(context, location, 1, 32)?; - - let array_counter_ptr = block.append_op_result( - ods::llvm::mlir_addressof( - context, - llvm::r#type::pointer(context, 0), - FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), - location, - ) - .into(), - )?; - - let array_counter = block.load(context, location, array_counter_ptr, array_ty)?; - - let value_counter = - block.extract_value(context, location, array_counter, u32_ty, libfunc_idx)?; - let value_incremented = block.addi(value_counter, k1, location)?; + let k1 = block.const_int(context, location, 0, 32)?; - let array_counter = block.insert_value( + let array_counter_ptr = + self.get_array_counter(context, module, block, location, libfuncs_amount)?; + let value_counter_ptr = block.gep( context, location, - array_counter, - value_incremented, - libfunc_idx, + array_counter_ptr, + &[GepIndex::Const(libfunc_idx as i32)], + u32_ty, )?; - block.store(context, location, array_counter_ptr, array_counter)?; + let value_counter = block.load(context, location, value_counter_ptr, u32_ty)?; + let value_incremented = block.addi(value_counter, k1, location)?; + + block.store(context, location, value_counter_ptr, value_incremented)?; Ok(()) } From f7b1aebac68f631e9d9229cc99e79d594b925f9e Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 4 Jul 2025 18:55:19 -0300 Subject: [PATCH 14/21] format --- src/bin/cairo-native-run.rs | 40 ++++----------------------------- src/metadata/libfunc_counter.rs | 8 +++++-- 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index c45cd9143..259a195bc 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -137,28 +137,12 @@ fn main() -> anyhow::Result<()> { } Box::new(move |function_id, args, gas, syscall_handler| { - let result = executor.invoke_dynamic_with_syscall_handler( + executor.invoke_dynamic_with_syscall_handler( function_id, args, gas, syscall_handler, - ); - - // Deallocate the array of counters - #[cfg(feature = "with-libfunc-counter")] - { - use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; - - if let Some(array_counter_ptr) = - executor.find_symbol_ptr(LibfuncCounterBinding::ArrayCounter.symbol()) - { - // unsafe { - // libc::free(array_counter_ptr); - // } - } - } - - result + ) }) } RunMode::Jit => { @@ -198,28 +182,12 @@ fn main() -> anyhow::Result<()> { } Box::new(move |function_id, args, gas, syscall_handler| { - let result = executor.invoke_dynamic_with_syscall_handler( + executor.invoke_dynamic_with_syscall_handler( function_id, args, gas, syscall_handler, - ); - - // Deallocate the array of counters - #[cfg(feature = "with-libfunc-counter")] - { - use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; - - if let Some(array_counter_ptr) = - executor.find_symbol_ptr(LibfuncCounterBinding::ArrayCounter.symbol()) - { - // unsafe { - // libc::free(*array_counter_ptr); - // } - } - } - - result + ) }) } }; diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 552f9799a..26070c57e 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -184,8 +184,12 @@ impl LibfuncCounterMeta { ) .into(), )?; - let array_counter_ptr = block.load(context, location, array_counter_ptr_ptr, llvm::r#type::pointer(context, 0))?; - + let array_counter_ptr = block.load( + context, + location, + array_counter_ptr_ptr, + llvm::r#type::pointer(context, 0), + )?; block.append_operation( OperationBuilder::new("llvm.call", location) From 71ad878f430ab4de70e1390f4a9004136038c846 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 7 Jul 2025 13:00:25 -0300 Subject: [PATCH 15/21] fix value increment --- src/bin/cairo-native-run.rs | 64 +++++++++++++++-- src/compiler.rs | 20 ------ src/executor/aot.rs | 3 - src/executor/contract.rs | 3 - src/executor/jit.rs | 3 - src/metadata/libfunc_counter.rs | 117 +++++++------------------------- 6 files changed, 86 insertions(+), 124 deletions(-) diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index 259a195bc..1cc0698d7 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -136,13 +136,40 @@ fn main() -> anyhow::Result<()> { } } + #[cfg(feature = "with-libfunc-counter")] + let libfuncs_amount = sierra_program.libfunc_declarations.len(); + Box::new(move |function_id, args, gas, syscall_handler| { - executor.invoke_dynamic_with_syscall_handler( + let result = executor.invoke_dynamic_with_syscall_handler( function_id, args, gas, syscall_handler, - ) + ); + + #[cfg(feature = "with-libfunc-counter")] + unsafe { + use cairo_native::metadata::libfunc_counter::{ + libfunc_counter_runtime, LibfuncCounterBinding, + }; + + let counter_id_ptr = executor + .find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) + .unwrap() + .cast::(); + let counters_array_ptr_ptr = executor + .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + .unwrap() + .cast::<*mut u32>(); + + libfunc_counter_runtime::store_counters_array( + counter_id_ptr, + counters_array_ptr_ptr, + libfuncs_amount, + ); + } + + result }) } RunMode::Jit => { @@ -152,6 +179,7 @@ fn main() -> anyhow::Result<()> { #[cfg(feature = "with-trace-dump")] { use cairo_native::metadata::trace_dump::TraceBinding; + if let Some(trace_id) = executor.find_symbol_ptr(TraceBinding::TraceId.symbol()) { let trace_id = trace_id.cast::(); unsafe { *trace_id = 0 }; @@ -173,6 +201,7 @@ fn main() -> anyhow::Result<()> { #[cfg(feature = "with-libfunc-counter")] { use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; + if let Some(counter_id) = executor.find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) { @@ -181,13 +210,40 @@ fn main() -> anyhow::Result<()> { } } + #[cfg(feature = "with-libfunc-counter")] + let libfuncs_amount = sierra_program.libfunc_declarations.len(); + Box::new(move |function_id, args, gas, syscall_handler| { - executor.invoke_dynamic_with_syscall_handler( + let result = executor.invoke_dynamic_with_syscall_handler( function_id, args, gas, syscall_handler, - ) + ); + + #[cfg(feature = "with-libfunc-counter")] + unsafe { + use cairo_native::metadata::libfunc_counter::{ + libfunc_counter_runtime, LibfuncCounterBinding, + }; + + let counter_id_ptr = executor + .find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) + .unwrap() + .cast::(); + let counters_array_ptr_ptr = executor + .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + .unwrap() + .cast::<*mut u32>(); + + libfunc_counter_runtime::store_counters_array( + counter_id_ptr, + counters_array_ptr_ptr, + libfuncs_amount, + ); + } + + result }) } }; diff --git a/src/compiler.rs b/src/compiler.rs index 14213cdb3..83e7dfec6 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1064,10 +1064,6 @@ fn compile_func( sierra_stmt_start_offset + function.entry_point.0, 0, ), - #[cfg(feature = "with-libfunc-counter")] - libfunc_indexes, - #[cfg(feature = "with-libfunc-counter")] - metadata, )?; tracing::debug!("Done generating function {}.", function.id); @@ -1449,8 +1445,6 @@ fn generate_entry_point_wrapper<'c>( arg_types: &[(Type<'c>, Location<'c>)], ret_types: &[Type<'c>], location: Location<'c>, - #[cfg(feature = "with-libfunc-counter")] libfunc_indexes: &HashMap, - #[cfg(feature = "with-libfunc-counter")] metadata: &mut MetadataStorage, ) -> Result<(), Error> { let region = Region::new(); let block = region.append_block(Block::new(arg_types)); @@ -1483,20 +1477,6 @@ fn generate_entry_point_wrapper<'c>( returns.push(block.extract_value(context, location, result, *ty, i)?); } - #[cfg(feature = "with-libfunc-counter")] - { - use crate::metadata::libfunc_counter::LibfuncCounterMeta; - - let libfunc_counter = metadata.get_mut::().unwrap(); - libfunc_counter.store_array_counter( - context, - module, - &block, - location, - libfunc_indexes.len() as u32, - )?; - } - block.append_operation(func::r#return(&returns, location)); module.body().append_operation(func::func( diff --git a/src/executor/aot.rs b/src/executor/aot.rs index 113810ee0..56ee1b2b1 100644 --- a/src/executor/aot.rs +++ b/src/executor/aot.rs @@ -63,9 +63,6 @@ impl AotNativeExecutor { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); - #[cfg(feature = "with-libfunc-counter")] - crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); - executor } diff --git a/src/executor/contract.rs b/src/executor/contract.rs index aeb059d50..833a4d24a 100644 --- a/src/executor/contract.rs +++ b/src/executor/contract.rs @@ -333,9 +333,6 @@ impl AotContractExecutor { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); - #[cfg(feature = "with-libfunc-counter")] - crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); - Ok(Some(executor)) } diff --git a/src/executor/jit.rs b/src/executor/jit.rs index af38f8d36..f7cbe58c7 100644 --- a/src/executor/jit.rs +++ b/src/executor/jit.rs @@ -74,9 +74,6 @@ impl<'m> JitNativeExecutor<'m> { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); - #[cfg(feature = "with-libfunc-counter")] - crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); - Ok(executor) } diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 26070c57e..07e393fd2 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -20,13 +20,12 @@ //! contract. Since a contract can call other contracts, we need a way of restoring the counter after every execution. //! //! See `cairo-native-run` for an example on how to do it. -use std::{collections::HashSet, os::raw::c_void, ptr}; +use std::collections::HashSet; use melior::{ dialect::{llvm, memref, ods}, ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, - operation::OperationBuilder, r#type::{IntegerType, MemRefType}, Attribute, Block, BlockLike, Location, Module, Region, Value, }, @@ -41,27 +40,15 @@ use crate::{ #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum LibfuncCounterBinding { - StoreArrayCounter, CounterId, - ArrayCounter, + CounterArray, } impl LibfuncCounterBinding { pub const fn symbol(self) -> &'static str { match self { - LibfuncCounterBinding::StoreArrayCounter => "cairo_native__store_array_counter", LibfuncCounterBinding::CounterId => "cairo_native__counter_id", - LibfuncCounterBinding::ArrayCounter => "cairo_native__array_counter", - } - } - - const fn function_ptr(self) -> *const () { - match self { - LibfuncCounterBinding::StoreArrayCounter => { - libfunc_counter_runtime::store_array_counter as *const () - } - LibfuncCounterBinding::CounterId => ptr::null(), - LibfuncCounterBinding::ArrayCounter => ptr::null(), + LibfuncCounterBinding::CounterArray => "cairo_native__counter_array", } } } @@ -155,54 +142,8 @@ impl LibfuncCounterMeta { block.append_op_result(memref::load(libfunc_counter_id_ptr, &[], location)) } - /// Indexes the array of counters and increments the counter relative - /// to the given libfunc index - pub fn store_array_counter( - &mut self, - context: &Context, - module: &Module, - block: &Block<'_>, - location: Location, - libfunc_amount: u32, - ) -> Result<()> { - let counter_id = self.build_counter_id(context, module, block, location)?; - let function_ptr = self.build_function( - context, - module, - block, - location, - LibfuncCounterBinding::StoreArrayCounter, - )?; - let lifuncs_amount = block.const_int(context, location, libfunc_amount, 32)?; - // by this time, the array counter should be initialized - let array_counter_ptr_ptr = block.append_op_result( - ods::llvm::mlir_addressof( - context, - llvm::r#type::pointer(context, 0), - FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), - location, - ) - .into(), - )?; - let array_counter_ptr = block.load( - context, - location, - array_counter_ptr_ptr, - llvm::r#type::pointer(context, 0), - )?; - - block.append_operation( - OperationBuilder::new("llvm.call", location) - .add_operands(&[function_ptr]) - .add_operands(&[counter_id, array_counter_ptr, lifuncs_amount]) - .build()?, - ); - - Ok(()) - } - /// Build the array of counters - fn get_array_counter<'c, 'a>( + fn build_array_counter<'c, 'a>( &mut self, context: &'c Context, module: &Module, @@ -210,13 +151,15 @@ impl LibfuncCounterMeta { location: Location<'c>, libfunc_amount: u32, ) -> Result> { - if self.active_map.insert(LibfuncCounterBinding::ArrayCounter) { + if self.active_map.insert(LibfuncCounterBinding::CounterArray) { + self.build_counter_id(context, module, block, location)?; + module.body().append_operation( ods::llvm::mlir_global( context, Region::new(), TypeAttribute::new(llvm::r#type::pointer(context, 0)), - StringAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), + StringAttribute::new(context, LibfuncCounterBinding::CounterArray.symbol()), Attribute::parse(context, "#llvm.linkage") .ok_or(Error::ParseAttributeError)?, location, @@ -240,7 +183,7 @@ impl LibfuncCounterMeta { llvm::r#type::pointer(context, 0), FlatSymbolRefAttribute::new( context, - LibfuncCounterBinding::ArrayCounter.symbol(), + LibfuncCounterBinding::CounterArray.symbol(), ), location, ) @@ -264,13 +207,13 @@ impl LibfuncCounterMeta { ods::llvm::mlir_addressof( context, llvm::r#type::pointer(context, 0), - FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::ArrayCounter.symbol()), + FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::CounterArray.symbol()), location, ) .into(), )?; - // // return the pointer to array counter + // return the pointer to array counter block.load( context, location, @@ -289,10 +232,11 @@ impl LibfuncCounterMeta { libfuncs_amount: u32, ) -> Result<()> { let u32_ty = IntegerType::new(context, 32).into(); - let k1 = block.const_int(context, location, 0, 32)?; + let k1 = block.const_int(context, location, 1, 32)?; let array_counter_ptr = - self.get_array_counter(context, module, block, location, libfuncs_amount)?; + self.build_array_counter(context, module, block, location, libfuncs_amount)?; + let value_counter_ptr = block.gep( context, location, @@ -310,24 +254,13 @@ impl LibfuncCounterMeta { } } -pub fn setup_runtime(find_symbol_ptr: impl Fn(&str) -> Option<*mut c_void>) { - let bindings = &[LibfuncCounterBinding::StoreArrayCounter]; - - for binding in bindings { - if let Some(global) = find_symbol_ptr(binding.symbol()) { - let global = global.cast::<*const ()>(); - unsafe { *global = binding.function_ptr() }; - } - } -} - pub mod libfunc_counter_runtime { + use core::slice; use std::{ collections::HashMap, sync::{LazyLock, Mutex}, }; - use itertools::Itertools; use melior::{ ir::{Block, Location, Module}, Context, @@ -364,16 +297,18 @@ pub mod libfunc_counter_runtime { ) } - pub unsafe extern "C" fn store_array_counter( - counter_id: u64, - array_counter: *const u32, - libfuncs_amount: u32, + pub unsafe fn store_counters_array( + counter_id_ptr: *mut u64, + array_ptr_ptr: *mut *mut u32, + libfuncs_amount: usize, ) { - let mut libfunc_counter = LIBFUNC_COUNTER.lock().unwrap(); - let vec = (0..libfuncs_amount) - .map(|i| *array_counter.add(i as usize)) - .collect_vec(); + let counters_vec = slice::from_raw_parts(*array_ptr_ptr, libfuncs_amount).to_vec(); + + LIBFUNC_COUNTER + .lock() + .unwrap() + .insert(*counter_id_ptr, counters_vec); - libfunc_counter.insert(counter_id, vec); + libc::free(*array_ptr_ptr.cast::<*mut libc::c_void>()); } } From caa1d071aa9674e59f35df1f32b568c50971fd87 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 11 Jul 2025 12:27:40 -0300 Subject: [PATCH 16/21] implement array counter guards --- src/bin/cairo-native-run.rs | 124 +++++++++++++++++++++----------- src/executor/jit.rs | 2 +- src/metadata/libfunc_counter.rs | 44 +++++++++++- 3 files changed, 127 insertions(+), 43 deletions(-) diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index 1cc0698d7..8e96dc1f1 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -6,6 +6,10 @@ use cairo_lang_runner::short_string::as_cairo_short_string; #[cfg(feature = "with-libfunc-profiling")] use cairo_lang_sierra::ids::ConcreteLibfuncId; use cairo_lang_sierra_to_casm::metadata::MetadataComputationConfig; +#[cfg(feature = "with-libfunc-counter")] +use cairo_native::metadata::libfunc_counter::{ + libfunc_counter_runtime::CountersArrayGuard, LibfuncCounterBinding, +}; #[cfg(feature = "with-libfunc-profiling")] use cairo_native::metadata::profiler::LibfuncProfileData; use cairo_native::{ @@ -111,6 +115,9 @@ fn main() -> anyhow::Result<()> { .compile(&sierra_program, false, Some(Default::default()), None) .unwrap(); + #[cfg(feature = "with-libfunc-counter")] + let libfuncs_amount = sierra_program.clone().libfunc_declarations.len(); + let native_executor: Box _> = match args.run_mode { RunMode::Aot => { let executor = @@ -136,10 +143,21 @@ fn main() -> anyhow::Result<()> { } } - #[cfg(feature = "with-libfunc-counter")] - let libfuncs_amount = sierra_program.libfunc_declarations.len(); - Box::new(move |function_id, args, gas, syscall_handler| { + #[cfg(feature = "with-libfunc-counter")] + let mut array_counter_guard = CountersArrayGuard(std::ptr::null_mut()); + + #[cfg(feature = "with-libfunc-counter")] + if let Some(counter_array_ptr_ptr) = + executor.find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + { + let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); + unsafe { + // Save the current then array of counters to restore it after the execution. + array_counter_guard = CountersArrayGuard::init(*counter_array_ptr_ptr); + } + }; + let result = executor.invoke_dynamic_with_syscall_handler( function_id, args, @@ -148,26 +166,34 @@ fn main() -> anyhow::Result<()> { ); #[cfg(feature = "with-libfunc-counter")] - unsafe { - use cairo_native::metadata::libfunc_counter::{ - libfunc_counter_runtime, LibfuncCounterBinding, - }; + { + use cairo_native::metadata::libfunc_counter::libfunc_counter_runtime; + use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; + + let counter_array_ptr_ptr = executor + .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + .expect(""); + let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); let counter_id_ptr = executor - .find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) - .unwrap() - .cast::(); - let counters_array_ptr_ptr = executor .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) - .unwrap() - .cast::<*mut u32>(); - - libfunc_counter_runtime::store_counters_array( - counter_id_ptr, - counters_array_ptr_ptr, - libfuncs_amount, - ); - } + .expect(""); + + unsafe { + libfunc_counter_runtime::store_counters_array( + counter_id_ptr as *mut u64, + counter_array_ptr_ptr, + libfuncs_amount, + ); + + if !(*counter_array_ptr_ptr).is_null() { + libc::free(*counter_array_ptr_ptr as *mut libc::c_void); + } + *counter_array_ptr_ptr = array_counter_guard.0; + } + + drop(array_counter_guard); + }; result }) @@ -210,10 +236,20 @@ fn main() -> anyhow::Result<()> { } } - #[cfg(feature = "with-libfunc-counter")] - let libfuncs_amount = sierra_program.libfunc_declarations.len(); - Box::new(move |function_id, args, gas, syscall_handler| { + #[cfg(feature = "with-libfunc-counter")] + let mut array_counter_guard = CountersArrayGuard(std::ptr::null_mut()); + + #[cfg(feature = "with-libfunc-counter")] + if let Some(counter_array_ptr_ptr) = + executor.find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + { + let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); + unsafe { + array_counter_guard = CountersArrayGuard::init(*counter_array_ptr_ptr); + } + }; + let result = executor.invoke_dynamic_with_syscall_handler( function_id, args, @@ -222,26 +258,34 @@ fn main() -> anyhow::Result<()> { ); #[cfg(feature = "with-libfunc-counter")] - unsafe { - use cairo_native::metadata::libfunc_counter::{ - libfunc_counter_runtime, LibfuncCounterBinding, - }; + { + use cairo_native::metadata::libfunc_counter::libfunc_counter_runtime; + use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; + let counter_array_ptr_ptr = executor + .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + .expect(""); + + let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); let counter_id_ptr = executor - .find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) - .unwrap() - .cast::(); - let counters_array_ptr_ptr = executor .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) - .unwrap() - .cast::<*mut u32>(); - - libfunc_counter_runtime::store_counters_array( - counter_id_ptr, - counters_array_ptr_ptr, - libfuncs_amount, - ); - } + .expect(""); + + unsafe { + libfunc_counter_runtime::store_counters_array( + counter_id_ptr as *mut u64, + counter_array_ptr_ptr, + libfuncs_amount, + ); + + if !(*counter_array_ptr_ptr).is_null() { + libc::free(*counter_array_ptr_ptr as *mut libc::c_void); + } + *counter_array_ptr_ptr = array_counter_guard.0; + } + + drop(array_counter_guard); + }; result }) diff --git a/src/executor/jit.rs b/src/executor/jit.rs index f7cbe58c7..73c832d26 100644 --- a/src/executor/jit.rs +++ b/src/executor/jit.rs @@ -120,7 +120,7 @@ impl<'m> JitNativeExecutor<'m> { .gas_metadata .get_initial_available_gas(function_id, gas) .map_err(crate::error::Error::GasMetadataError)?; - + super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 07e393fd2..1388e7614 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -41,6 +41,7 @@ use crate::{ #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum LibfuncCounterBinding { CounterId, + GetCounterArray, CounterArray, } @@ -48,9 +49,20 @@ impl LibfuncCounterBinding { pub const fn symbol(self) -> &'static str { match self { LibfuncCounterBinding::CounterId => "cairo_native__counter_id", + LibfuncCounterBinding::GetCounterArray => "cairo_native__get_counter_array", LibfuncCounterBinding::CounterArray => "cairo_native__counter_array", } } + + pub const fn function_ptr(self) -> *const () { + match self { + LibfuncCounterBinding::CounterId => std::ptr::null(), + LibfuncCounterBinding::GetCounterArray => { + libfunc_counter_runtime::get_counter_array as *const () + } + LibfuncCounterBinding::CounterArray => std::ptr::null(), + } + } } #[derive(Clone, Default)] @@ -257,6 +269,7 @@ impl LibfuncCounterMeta { pub mod libfunc_counter_runtime { use core::slice; use std::{ + cell::Cell, collections::HashMap, sync::{LazyLock, Mutex}, }; @@ -275,6 +288,31 @@ pub mod libfunc_counter_runtime { pub static LIBFUNC_COUNTER: LazyLock>>> = LazyLock::new(|| Mutex::new(HashMap::new())); + thread_local! { + pub(crate) static COUNTERS_ARRAY: Cell<*mut u32> = const { + // This value will be overritten before executing the code + Cell::new(std::ptr::null_mut()) + }; + } + + /// In the context of Starknet, a contract may call another. This means we + /// need as many arrays of counters as call contracts are invoked during execution. + /// This struct is used to hold the current array before calling the next contract + /// so that it can then be restored. + pub struct CountersArrayGuard(pub *mut u32); + + impl CountersArrayGuard { + pub fn init(array_ptr: *mut u32) -> CountersArrayGuard { + Self(COUNTERS_ARRAY.replace(array_ptr)) + } + } + + impl Drop for CountersArrayGuard { + fn drop(&mut self) { + COUNTERS_ARRAY.set(self.0); + } + } + /// Increase the libfunc's counter given its index pub fn count_libfunc( context: &Context, @@ -297,6 +335,10 @@ pub mod libfunc_counter_runtime { ) } + pub extern "C" fn get_counter_array() -> *const u32 { + COUNTERS_ARRAY.with(|x| x.as_ptr()) as *const u32 + } + pub unsafe fn store_counters_array( counter_id_ptr: *mut u64, array_ptr_ptr: *mut *mut u32, @@ -308,7 +350,5 @@ pub mod libfunc_counter_runtime { .lock() .unwrap() .insert(*counter_id_ptr, counters_vec); - - libc::free(*array_ptr_ptr.cast::<*mut libc::c_void>()); } } From 3a35a0ecc5b5b615a7953d677e0ad8d8a8a710d4 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 15 Jul 2025 10:11:02 -0300 Subject: [PATCH 17/21] add support for multiple contract inner calls --- counter.json | 45 ++++++++ counter1.json | 196 ++++++++++++++++++++++++++++++++ src/bin/cairo-native-run.rs | 59 ++-------- src/compiler.rs | 1 - src/executor/aot.rs | 3 + src/executor/contract.rs | 3 + src/executor/jit.rs | 5 +- src/metadata/libfunc_counter.rs | 144 ++++++++--------------- 8 files changed, 307 insertions(+), 149 deletions(-) create mode 100644 counter.json create mode 100644 counter1.json diff --git a/counter.json b/counter.json new file mode 100644 index 000000000..e7bb1df92 --- /dev/null +++ b/counter.json @@ -0,0 +1,45 @@ +{ + "store_temp": 2000015, + "dup": 4000022, + "enum_init, 1>": 0, + "store_temp>": 2, + "felt252_add": 2000010, + "struct_construct": 1, + "felt252_is_zero": 2000014, + "const_as_immediate>": 2, + "store_temp": 4000027, + "array_new": 0, + "array_append": 0, + "const_as_immediate>": 1, + "store_temp>>": 0, + "const_as_immediate>": 0, + "struct_construct": 0, + "store_temp>": 1, + "drop": 4, + "enum_init, 1>": 0, + "enum_init, 0>": 2, + "store_temp": 6000039, + "function_call": 0, + "const_as_immediate>": 2000012, + "enum_match>": 2, + "function_call>": 0, + "drop>": 2000010, + "felt252_sub": 2000012, + "struct_construct>>": 0, + "struct_construct>": 2, + "const_as_immediate>": 1, + "struct_construct>": 1, + "enum_init, 0>": 1, + "struct_deconstruct>": 2, + "function_call>": 0, + "const_as_immediate>": 1, + "const_as_immediate>": 0, + "redeposit_gas": 2000013, + "const_as_immediate>": 1, + "function_call": 2000012, + "branch_align": 4000028, + "function_call>": 0, + "withdraw_gas": 2000012, + "const_as_immediate>": 0, + "disable_ap_tracking": 2000013 +} \ No newline at end of file diff --git a/counter1.json b/counter1.json new file mode 100644 index 000000000..22eff463b --- /dev/null +++ b/counter1.json @@ -0,0 +1,196 @@ +disable_ap_tracking + Total Samples: 1999987 + Total Execution Time: 862781 + Average Execution Time: 0.43139330405647636 + Standard Deviation: 0.24558112313988842 + Quartiles: [0, 0, 0, 1, 2] + +withdraw_gas + Total Samples: 1752323 + Total Execution Time: 1752323 + Average Execution Time: 1 + Standard Deviation: 0 + Quartiles: [1, 1, 1, 1, 1] + +branch_align + Total Samples: 3999966 + Total Execution Time: 1724229 + Average Execution Time: 0.43106091401776914 + Standard Deviation: 0.24553740490159323 + Quartiles: [0, 0, 0, 1, 2] + +dup + Total Samples: 3999962 + Total Execution Time: 1723783 + Average Execution Time: 0.4309498440235182 + Standard Deviation: 0.24551657865406767 + Quartiles: [0, 0, 0, 1, 2] + +store_temp + Total Samples: 3999958 + Total Execution Time: 1725644 + Average Execution Time: 0.4314155298630636 + Standard Deviation: 0.2455991736473679 + Quartiles: [0, 0, 0, 1, 2] + +felt252_is_zero + Total Samples: 1999978 + Total Execution Time: 923925 + Average Execution Time: 0.4619675816433981 + Standard Deviation: 0.24889653892574365 + Quartiles: [0, 0, 0, 1, 2] + +drop + Total Samples: 4 + Total Execution Time: 1 + Average Execution Time: 0.25 + Standard Deviation: 0.1875 + Quartiles: [0, 0, 0, 1, 1] + +redeposit_gas + Total Samples: 1728671 + Total Execution Time: 1728671 + Average Execution Time: 1 + Standard Deviation: 0 + Quartiles: [1, 1, 1, 1, 1] + +struct_construct> + Total Samples: 2 + Total Execution Time: 1 + Average Execution Time: 0.5 + Standard Deviation: 0.25 + Quartiles: [0, 0, 1, 1, 1] + +enum_init, 0> + Total Samples: 2 + Total Execution Time: 6 + Average Execution Time: 3 + Standard Deviation: 4 + Quartiles: [1, 1, 5, 5, 5] + +store_temp + Total Samples: 1999990 + Total Execution Time: 862916 + Average Execution Time: 0.4314601573007865 + Standard Deviation: 0.24558129135247406 + Quartiles: [0, 0, 0, 1, 2] + +store_temp> + Total Samples: 2 + Total Execution Time: 0 + Average Execution Time: 0 + Standard Deviation: 0 + Quartiles: [0, 0, 0, 0, 0] + +drop> + Total Samples: 1999984 + Total Execution Time: 863143 + Average Execution Time: 0.4315749525996208 + Standard Deviation: 0.24561601526674237 + Quartiles: [0, 0, 0, 1, 2] + +felt252_add + Total Samples: 1999988 + Total Execution Time: 1000719 + Average Execution Time: 0.5003625021750131 + Standard Deviation: 0.2503738708460318 + Quartiles: [0, 0, 1, 1, 2] + +const_as_immediate> + Total Samples: 1999993 + Total Execution Time: 861915 + Average Execution Time: 0.4309590083565292 + Standard Deviation: 0.24551934247734983 + Quartiles: [0, 0, 0, 1, 2] + +felt252_sub + Total Samples: 1999971 + Total Execution Time: 1001302 + Average Execution Time: 0.5006582595447634 + Standard Deviation: 0.250352571805831 + Quartiles: [0, 0, 1, 1, 2] + +store_temp + Total Samples: 5999955 + Total Execution Time: 2585480 + Average Execution Time: 0.4309165652075724 + Standard Deviation: 0.24551881457356997 + Quartiles: [0, 0, 0, 1, 2] + +const_as_immediate> + Total Samples: 2 + Total Execution Time: 1 + Average Execution Time: 0.5 + Standard Deviation: 0.25 + Quartiles: [0, 0, 1, 1, 1] + +const_as_immediate> + Total Samples: 1 + Total Execution Time: 0 + Average Execution Time: 0 + Standard Deviation: 0 + Quartiles: [0, 0, 0, 0, 0] + +enum_match> + Total Samples: 2 + Total Execution Time: 5 + Average Execution Time: 2.5 + Standard Deviation: 6.25 + Quartiles: [0, 0, 5, 5, 5] + +struct_deconstruct> + Total Samples: 2 + Total Execution Time: 1 + Average Execution Time: 0.5 + Standard Deviation: 0.25 + Quartiles: [0, 0, 1, 1, 1] + +const_as_immediate> + Total Samples: 1 + Total Execution Time: 1 + Average Execution Time: 1 + Standard Deviation: 0 + Quartiles: [1, 1, 1, 1, 1] + +const_as_immediate> + Total Samples: 1 + Total Execution Time: 0 + Average Execution Time: 0 + Standard Deviation: 0 + Quartiles: [0, 0, 0, 0, 0] + +const_as_immediate> + Total Samples: 1 + Total Execution Time: 0 + Average Execution Time: 0 + Standard Deviation: 0 + Quartiles: [0, 0, 0, 0, 0] + +struct_construct + Total Samples: 1 + Total Execution Time: 1 + Average Execution Time: 1 + Standard Deviation: 0 + Quartiles: [1, 1, 1, 1, 1] + +struct_construct> + Total Samples: 1 + Total Execution Time: 1 + Average Execution Time: 1 + Standard Deviation: 0 + Quartiles: [1, 1, 1, 1, 1] + +enum_init, 0> + Total Samples: 1 + Total Execution Time: 0 + Average Execution Time: 0 + Standard Deviation: 0 + Quartiles: [0, 0, 0, 0, 0] + +store_temp> + Total Samples: 1 + Total Execution Time: 1 + Average Execution Time: 1 + Standard Deviation: 0 + Quartiles: [1, 1, 1, 1, 1] + diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index 8e96dc1f1..af8a9025c 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -7,9 +7,7 @@ use cairo_lang_runner::short_string::as_cairo_short_string; use cairo_lang_sierra::ids::ConcreteLibfuncId; use cairo_lang_sierra_to_casm::metadata::MetadataComputationConfig; #[cfg(feature = "with-libfunc-counter")] -use cairo_native::metadata::libfunc_counter::{ - libfunc_counter_runtime::CountersArrayGuard, LibfuncCounterBinding, -}; +use cairo_native::metadata::libfunc_counter::libfunc_counter_runtime::CountersArrayGuard; #[cfg(feature = "with-libfunc-profiling")] use cairo_native::metadata::profiler::LibfuncProfileData; use cairo_native::{ @@ -145,18 +143,7 @@ fn main() -> anyhow::Result<()> { Box::new(move |function_id, args, gas, syscall_handler| { #[cfg(feature = "with-libfunc-counter")] - let mut array_counter_guard = CountersArrayGuard(std::ptr::null_mut()); - - #[cfg(feature = "with-libfunc-counter")] - if let Some(counter_array_ptr_ptr) = - executor.find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) - { - let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); - unsafe { - // Save the current then array of counters to restore it after the execution. - array_counter_guard = CountersArrayGuard::init(*counter_array_ptr_ptr); - } - }; + let array_counter_guard = CountersArrayGuard::init(libfuncs_amount); let result = executor.invoke_dynamic_with_syscall_handler( function_id, @@ -170,26 +157,15 @@ fn main() -> anyhow::Result<()> { use cairo_native::metadata::libfunc_counter::libfunc_counter_runtime; use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; - let counter_array_ptr_ptr = executor - .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) - .expect(""); - - let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); let counter_id_ptr = executor - .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + .find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) .expect(""); unsafe { - libfunc_counter_runtime::store_counters_array( + libfunc_counter_runtime::store_and_free_counters_array( counter_id_ptr as *mut u64, - counter_array_ptr_ptr, libfuncs_amount, ); - - if !(*counter_array_ptr_ptr).is_null() { - libc::free(*counter_array_ptr_ptr as *mut libc::c_void); - } - *counter_array_ptr_ptr = array_counter_guard.0; } drop(array_counter_guard); @@ -238,17 +214,7 @@ fn main() -> anyhow::Result<()> { Box::new(move |function_id, args, gas, syscall_handler| { #[cfg(feature = "with-libfunc-counter")] - let mut array_counter_guard = CountersArrayGuard(std::ptr::null_mut()); - - #[cfg(feature = "with-libfunc-counter")] - if let Some(counter_array_ptr_ptr) = - executor.find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) - { - let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); - unsafe { - array_counter_guard = CountersArrayGuard::init(*counter_array_ptr_ptr); - } - }; + let array_counter_guard = CountersArrayGuard::init(libfuncs_amount); let result = executor.invoke_dynamic_with_syscall_handler( function_id, @@ -262,26 +228,15 @@ fn main() -> anyhow::Result<()> { use cairo_native::metadata::libfunc_counter::libfunc_counter_runtime; use cairo_native::metadata::libfunc_counter::LibfuncCounterBinding; - let counter_array_ptr_ptr = executor - .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) - .expect(""); - - let counter_array_ptr_ptr = counter_array_ptr_ptr.cast::<*mut u32>(); let counter_id_ptr = executor - .find_symbol_ptr(LibfuncCounterBinding::CounterArray.symbol()) + .find_symbol_ptr(LibfuncCounterBinding::CounterId.symbol()) .expect(""); unsafe { - libfunc_counter_runtime::store_counters_array( + libfunc_counter_runtime::store_and_free_counters_array( counter_id_ptr as *mut u64, - counter_array_ptr_ptr, libfuncs_amount, ); - - if !(*counter_array_ptr_ptr).is_null() { - libc::free(*counter_array_ptr_ptr as *mut libc::c_void); - } - *counter_array_ptr_ptr = array_counter_guard.0; } drop(array_counter_guard); diff --git a/src/compiler.rs b/src/compiler.rs index 83e7dfec6..9de3925c2 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -667,7 +667,6 @@ fn compile_func( location, metadata, *libfunc_idx, - libfunc_indexes.len() as u32, )?; } diff --git a/src/executor/aot.rs b/src/executor/aot.rs index 56ee1b2b1..113810ee0 100644 --- a/src/executor/aot.rs +++ b/src/executor/aot.rs @@ -63,6 +63,9 @@ impl AotNativeExecutor { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); + #[cfg(feature = "with-libfunc-counter")] + crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); + executor } diff --git a/src/executor/contract.rs b/src/executor/contract.rs index 833a4d24a..aeb059d50 100644 --- a/src/executor/contract.rs +++ b/src/executor/contract.rs @@ -333,6 +333,9 @@ impl AotContractExecutor { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); + #[cfg(feature = "with-libfunc-counter")] + crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); + Ok(Some(executor)) } diff --git a/src/executor/jit.rs b/src/executor/jit.rs index 73c832d26..af38f8d36 100644 --- a/src/executor/jit.rs +++ b/src/executor/jit.rs @@ -74,6 +74,9 @@ impl<'m> JitNativeExecutor<'m> { #[cfg(feature = "with-libfunc-profiling")] crate::metadata::profiler::setup_runtime(|name| executor.find_symbol_ptr(name)); + #[cfg(feature = "with-libfunc-counter")] + crate::metadata::libfunc_counter::setup_runtime(|name| executor.find_symbol_ptr(name)); + Ok(executor) } @@ -120,7 +123,7 @@ impl<'m> JitNativeExecutor<'m> { .gas_metadata .get_initial_available_gas(function_id, gas) .map_err(crate::error::Error::GasMetadataError)?; - + super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 1388e7614..550340a70 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -26,6 +26,7 @@ use melior::{ dialect::{llvm, memref, ods}, ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, + operation::OperationBuilder, r#type::{IntegerType, MemRefType}, Attribute, Block, BlockLike, Location, Module, Region, Value, }, @@ -34,15 +35,13 @@ use melior::{ use crate::{ error::{Error, Result}, - metadata::realloc_bindings::ReallocBindingsMeta, - utils::{get_integer_layout, layout_repeat, BlockExt, GepIndex}, + utils::{BlockExt, GepIndex}, }; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum LibfuncCounterBinding { CounterId, GetCounterArray, - CounterArray, } impl LibfuncCounterBinding { @@ -50,7 +49,6 @@ impl LibfuncCounterBinding { match self { LibfuncCounterBinding::CounterId => "cairo_native__counter_id", LibfuncCounterBinding::GetCounterArray => "cairo_native__get_counter_array", - LibfuncCounterBinding::CounterArray => "cairo_native__counter_array", } } @@ -60,7 +58,6 @@ impl LibfuncCounterBinding { LibfuncCounterBinding::GetCounterArray => { libfunc_counter_runtime::get_counter_array as *const () } - LibfuncCounterBinding::CounterArray => std::ptr::null(), } } } @@ -161,76 +158,21 @@ impl LibfuncCounterMeta { module: &Module, block: &'a Block<'c>, location: Location<'c>, - libfunc_amount: u32, ) -> Result> { - if self.active_map.insert(LibfuncCounterBinding::CounterArray) { - self.build_counter_id(context, module, block, location)?; - - module.body().append_operation( - ods::llvm::mlir_global( - context, - Region::new(), - TypeAttribute::new(llvm::r#type::pointer(context, 0)), - StringAttribute::new(context, LibfuncCounterBinding::CounterArray.symbol()), - Attribute::parse(context, "#llvm.linkage") - .ok_or(Error::ParseAttributeError)?, - location, - ) - .into(), - ); - - // Once we created the global pointer to the counters, we need to reallocate it so that it - // can hold as many counters as libfuncs declared - let u32_layout = get_integer_layout(32); - let libfuncs_amount_bytes = layout_repeat(&u32_layout, libfunc_amount as usize)? - .0 - .pad_to_align() - .size(); - let libfuncs_amount_bytes = - block.const_int(context, location, libfuncs_amount_bytes, 64)?; - - let array_counter_ptr_ptr = block.append_op_result( - ods::llvm::mlir_addressof( - context, - llvm::r#type::pointer(context, 0), - FlatSymbolRefAttribute::new( - context, - LibfuncCounterBinding::CounterArray.symbol(), - ), - location, - ) - .into(), - )?; - - let array_counter_ptr = - block.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?; - - let array_counter_ptr = block.append_op_result(ReallocBindingsMeta::realloc( - context, - array_counter_ptr, - libfuncs_amount_bytes, - location, - )?)?; - - block.store(context, location, array_counter_ptr_ptr, array_counter_ptr)?; - } - - let array_counter_ptr_ptr = block.append_op_result( - ods::llvm::mlir_addressof( - context, - llvm::r#type::pointer(context, 0), - FlatSymbolRefAttribute::new(context, LibfuncCounterBinding::CounterArray.symbol()), - location, - ) - .into(), - )?; - - // return the pointer to array counter - block.load( + self.build_counter_id(context, module, block, location)?; + let function_ptr = self.build_function( context, + module, + block, location, - array_counter_ptr_ptr, - llvm::r#type::pointer(context, 0), + LibfuncCounterBinding::GetCounterArray, + )?; + + block.append_op_result( + OperationBuilder::new("llvm.call", location) + .add_operands(&[function_ptr]) + .add_results(&[llvm::r#type::pointer(context, 0)]) + .build()?, ) } @@ -241,13 +183,11 @@ impl LibfuncCounterMeta { block: &Block<'_>, location: Location, libfunc_idx: usize, - libfuncs_amount: u32, ) -> Result<()> { let u32_ty = IntegerType::new(context, 32).into(); let k1 = block.const_int(context, location, 1, 32)?; - let array_counter_ptr = - self.build_array_counter(context, module, block, location, libfuncs_amount)?; + let array_counter_ptr = self.build_array_counter(context, module, block, location)?; let value_counter_ptr = block.gep( context, @@ -266,6 +206,17 @@ impl LibfuncCounterMeta { } } +pub fn setup_runtime(find_symbol_ptr: impl Fn(&str) -> Option<*mut libc::c_void>) { + let bindings = &[LibfuncCounterBinding::GetCounterArray]; + + for binding in bindings { + if let Some(global) = find_symbol_ptr(binding.symbol()) { + let global = global.cast::<*const ()>(); + unsafe { *global = binding.function_ptr() }; + } + } +} + pub mod libfunc_counter_runtime { use core::slice; use std::{ @@ -297,13 +248,23 @@ pub mod libfunc_counter_runtime { /// In the context of Starknet, a contract may call another. This means we /// need as many arrays of counters as call contracts are invoked during execution. - /// This struct is used to hold the current array before calling the next contract + /// This struct is used to hold the current array before calling the next contract /// so that it can then be restored. pub struct CountersArrayGuard(pub *mut u32); impl CountersArrayGuard { - pub fn init(array_ptr: *mut u32) -> CountersArrayGuard { - Self(COUNTERS_ARRAY.replace(array_ptr)) + pub fn init(libfuncs_amount: usize) -> CountersArrayGuard { + let u32_libfuncs_amount = libfuncs_amount * 4; + + let new_array = unsafe { libc::malloc(u32_libfuncs_amount) as *mut u32 }; + + for i in 0..libfuncs_amount { + unsafe { + *(new_array.add(i)) = 0 + }; + } + + Self(COUNTERS_ARRAY.replace(new_array)) } } @@ -321,34 +282,27 @@ pub mod libfunc_counter_runtime { location: Location, metadata: &mut MetadataStorage, libfunc_idx: usize, - libfuncs_amount: u32, ) -> Result<()> { let libfunc_counter = metadata.get_or_insert_with(LibfuncCounterMeta::default); - libfunc_counter.count_libfunc( - context, - module, - block, - location, - libfunc_idx, - libfuncs_amount, - ) + libfunc_counter.count_libfunc(context, module, block, location, libfunc_idx) } - pub extern "C" fn get_counter_array() -> *const u32 { - COUNTERS_ARRAY.with(|x| x.as_ptr()) as *const u32 + pub fn get_counter_array() -> *mut u32 { + COUNTERS_ARRAY.with(|x| x.get()) as *mut u32 } - pub unsafe fn store_counters_array( - counter_id_ptr: *mut u64, - array_ptr_ptr: *mut *mut u32, - libfuncs_amount: usize, - ) { - let counters_vec = slice::from_raw_parts(*array_ptr_ptr, libfuncs_amount).to_vec(); + pub unsafe extern "C" fn store_and_free_counters_array(counter_id_ptr: *mut u64, libfuncs_amount: usize) { + let counter_array_ptr = get_counter_array(); + let counters_vec = slice::from_raw_parts(counter_array_ptr, libfuncs_amount).to_vec(); LIBFUNC_COUNTER .lock() .unwrap() .insert(*counter_id_ptr, counters_vec); + + if !(counter_array_ptr.is_null()) { + libc::free(counter_array_ptr as *mut libc::c_void); + } } } From d5a557dc93c13c5c073106cffa41a5c5822ce3c7 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 15 Jul 2025 10:11:55 -0300 Subject: [PATCH 18/21] add support for multiple contract inner calls --- counter.json | 45 -------- counter1.json | 196 -------------------------------- src/metadata/libfunc_counter.rs | 4 +- 3 files changed, 2 insertions(+), 243 deletions(-) delete mode 100644 counter.json delete mode 100644 counter1.json diff --git a/counter.json b/counter.json deleted file mode 100644 index e7bb1df92..000000000 --- a/counter.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "store_temp": 2000015, - "dup": 4000022, - "enum_init, 1>": 0, - "store_temp>": 2, - "felt252_add": 2000010, - "struct_construct": 1, - "felt252_is_zero": 2000014, - "const_as_immediate>": 2, - "store_temp": 4000027, - "array_new": 0, - "array_append": 0, - "const_as_immediate>": 1, - "store_temp>>": 0, - "const_as_immediate>": 0, - "struct_construct": 0, - "store_temp>": 1, - "drop": 4, - "enum_init, 1>": 0, - "enum_init, 0>": 2, - "store_temp": 6000039, - "function_call": 0, - "const_as_immediate>": 2000012, - "enum_match>": 2, - "function_call>": 0, - "drop>": 2000010, - "felt252_sub": 2000012, - "struct_construct>>": 0, - "struct_construct>": 2, - "const_as_immediate>": 1, - "struct_construct>": 1, - "enum_init, 0>": 1, - "struct_deconstruct>": 2, - "function_call>": 0, - "const_as_immediate>": 1, - "const_as_immediate>": 0, - "redeposit_gas": 2000013, - "const_as_immediate>": 1, - "function_call": 2000012, - "branch_align": 4000028, - "function_call>": 0, - "withdraw_gas": 2000012, - "const_as_immediate>": 0, - "disable_ap_tracking": 2000013 -} \ No newline at end of file diff --git a/counter1.json b/counter1.json deleted file mode 100644 index 22eff463b..000000000 --- a/counter1.json +++ /dev/null @@ -1,196 +0,0 @@ -disable_ap_tracking - Total Samples: 1999987 - Total Execution Time: 862781 - Average Execution Time: 0.43139330405647636 - Standard Deviation: 0.24558112313988842 - Quartiles: [0, 0, 0, 1, 2] - -withdraw_gas - Total Samples: 1752323 - Total Execution Time: 1752323 - Average Execution Time: 1 - Standard Deviation: 0 - Quartiles: [1, 1, 1, 1, 1] - -branch_align - Total Samples: 3999966 - Total Execution Time: 1724229 - Average Execution Time: 0.43106091401776914 - Standard Deviation: 0.24553740490159323 - Quartiles: [0, 0, 0, 1, 2] - -dup - Total Samples: 3999962 - Total Execution Time: 1723783 - Average Execution Time: 0.4309498440235182 - Standard Deviation: 0.24551657865406767 - Quartiles: [0, 0, 0, 1, 2] - -store_temp - Total Samples: 3999958 - Total Execution Time: 1725644 - Average Execution Time: 0.4314155298630636 - Standard Deviation: 0.2455991736473679 - Quartiles: [0, 0, 0, 1, 2] - -felt252_is_zero - Total Samples: 1999978 - Total Execution Time: 923925 - Average Execution Time: 0.4619675816433981 - Standard Deviation: 0.24889653892574365 - Quartiles: [0, 0, 0, 1, 2] - -drop - Total Samples: 4 - Total Execution Time: 1 - Average Execution Time: 0.25 - Standard Deviation: 0.1875 - Quartiles: [0, 0, 0, 1, 1] - -redeposit_gas - Total Samples: 1728671 - Total Execution Time: 1728671 - Average Execution Time: 1 - Standard Deviation: 0 - Quartiles: [1, 1, 1, 1, 1] - -struct_construct> - Total Samples: 2 - Total Execution Time: 1 - Average Execution Time: 0.5 - Standard Deviation: 0.25 - Quartiles: [0, 0, 1, 1, 1] - -enum_init, 0> - Total Samples: 2 - Total Execution Time: 6 - Average Execution Time: 3 - Standard Deviation: 4 - Quartiles: [1, 1, 5, 5, 5] - -store_temp - Total Samples: 1999990 - Total Execution Time: 862916 - Average Execution Time: 0.4314601573007865 - Standard Deviation: 0.24558129135247406 - Quartiles: [0, 0, 0, 1, 2] - -store_temp> - Total Samples: 2 - Total Execution Time: 0 - Average Execution Time: 0 - Standard Deviation: 0 - Quartiles: [0, 0, 0, 0, 0] - -drop> - Total Samples: 1999984 - Total Execution Time: 863143 - Average Execution Time: 0.4315749525996208 - Standard Deviation: 0.24561601526674237 - Quartiles: [0, 0, 0, 1, 2] - -felt252_add - Total Samples: 1999988 - Total Execution Time: 1000719 - Average Execution Time: 0.5003625021750131 - Standard Deviation: 0.2503738708460318 - Quartiles: [0, 0, 1, 1, 2] - -const_as_immediate> - Total Samples: 1999993 - Total Execution Time: 861915 - Average Execution Time: 0.4309590083565292 - Standard Deviation: 0.24551934247734983 - Quartiles: [0, 0, 0, 1, 2] - -felt252_sub - Total Samples: 1999971 - Total Execution Time: 1001302 - Average Execution Time: 0.5006582595447634 - Standard Deviation: 0.250352571805831 - Quartiles: [0, 0, 1, 1, 2] - -store_temp - Total Samples: 5999955 - Total Execution Time: 2585480 - Average Execution Time: 0.4309165652075724 - Standard Deviation: 0.24551881457356997 - Quartiles: [0, 0, 0, 1, 2] - -const_as_immediate> - Total Samples: 2 - Total Execution Time: 1 - Average Execution Time: 0.5 - Standard Deviation: 0.25 - Quartiles: [0, 0, 1, 1, 1] - -const_as_immediate> - Total Samples: 1 - Total Execution Time: 0 - Average Execution Time: 0 - Standard Deviation: 0 - Quartiles: [0, 0, 0, 0, 0] - -enum_match> - Total Samples: 2 - Total Execution Time: 5 - Average Execution Time: 2.5 - Standard Deviation: 6.25 - Quartiles: [0, 0, 5, 5, 5] - -struct_deconstruct> - Total Samples: 2 - Total Execution Time: 1 - Average Execution Time: 0.5 - Standard Deviation: 0.25 - Quartiles: [0, 0, 1, 1, 1] - -const_as_immediate> - Total Samples: 1 - Total Execution Time: 1 - Average Execution Time: 1 - Standard Deviation: 0 - Quartiles: [1, 1, 1, 1, 1] - -const_as_immediate> - Total Samples: 1 - Total Execution Time: 0 - Average Execution Time: 0 - Standard Deviation: 0 - Quartiles: [0, 0, 0, 0, 0] - -const_as_immediate> - Total Samples: 1 - Total Execution Time: 0 - Average Execution Time: 0 - Standard Deviation: 0 - Quartiles: [0, 0, 0, 0, 0] - -struct_construct - Total Samples: 1 - Total Execution Time: 1 - Average Execution Time: 1 - Standard Deviation: 0 - Quartiles: [1, 1, 1, 1, 1] - -struct_construct> - Total Samples: 1 - Total Execution Time: 1 - Average Execution Time: 1 - Standard Deviation: 0 - Quartiles: [1, 1, 1, 1, 1] - -enum_init, 0> - Total Samples: 1 - Total Execution Time: 0 - Average Execution Time: 0 - Standard Deviation: 0 - Quartiles: [0, 0, 0, 0, 0] - -store_temp> - Total Samples: 1 - Total Execution Time: 1 - Average Execution Time: 1 - Standard Deviation: 0 - Quartiles: [1, 1, 1, 1, 1] - diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 550340a70..825764527 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -288,11 +288,11 @@ pub mod libfunc_counter_runtime { libfunc_counter.count_libfunc(context, module, block, location, libfunc_idx) } - pub fn get_counter_array() -> *mut u32 { + pub extern "C" fn get_counter_array() -> *mut u32 { COUNTERS_ARRAY.with(|x| x.get()) as *mut u32 } - pub unsafe extern "C" fn store_and_free_counters_array(counter_id_ptr: *mut u64, libfuncs_amount: usize) { + pub unsafe fn store_and_free_counters_array(counter_id_ptr: *mut u64, libfuncs_amount: usize) { let counter_array_ptr = get_counter_array(); let counters_vec = slice::from_raw_parts(counter_array_ptr, libfuncs_amount).to_vec(); From f51a00e16effebf6661a50dd95982fec209550db Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 17 Jul 2025 11:07:35 -0300 Subject: [PATCH 19/21] update docs --- src/metadata/libfunc_counter.rs | 61 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 825764527..65ba2ccf0 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -1,23 +1,19 @@ #![cfg(feature = "with-libfunc-counter")] //! The libfunc counter feature is used to generate information counting how many time a libfunc has been called. //! -//! When this feature is used, the compiler will call three important methods: +//! When this feature is used, the compiler will call one main method: //! -//! 1. `count_libfunc`: called before every libfunc execution. +//! 1. `count_libfunc`: called before every libfunc execution. This method will handle the counting. Given a the index +//! of a libfunc (relative the its declaration order), it accesses the array of counters and updates the counter. //! -//! 2. `build_array_counter`: called only once to build the array of counters (one for each libfuncs). The order of -//! is based on the libfuncs' declaration order. +//! In the context of Starknet contracts, we need to add support for building the arrays of counters for multiple executions. +//! To do so, we need one important element, which must be set before every contract execution: //! -//! 3. `store_array_counter`: called before finishing each entrypoint execution. It transforms the MLIR array into a -//! Rust vector which is then stored in `LIBFUNC_COUNTER`, a static variable that registers the array of counters by -//! execution, along with its `counter_id` (which is relative to the execution). +//! * A counter to track the ID of the current array of counter, which gets updated every time we switch to another +//! contract. Since a contract can call other contracts, we need a way of restoring the counter after every execution. //! -//! In the context of Starknet contracts, we need to add support for building -//! the arrays of counters for multiple executions. To do so, we need one important element, which must be set before every contract -//! execution: -//! -//! A counter to track the ID of the current array of counter, which gets updated every time we switch to another -//! contract. Since a contract can call other contracts, we need a way of restoring the counter after every execution. +//! * An array-of-counter guard. Every time a new entrypoint is executed, a new array of counters needs to be created in order to isolate +//! each of them. The guard keeps the last array that was used to restore it once the inner entrypoint execution has finished. //! //! See `cairo-native-run` for an example on how to do it. use std::collections::HashSet; @@ -48,7 +44,7 @@ impl LibfuncCounterBinding { pub const fn symbol(self) -> &'static str { match self { LibfuncCounterBinding::CounterId => "cairo_native__counter_id", - LibfuncCounterBinding::GetCounterArray => "cairo_native__get_counter_array", + LibfuncCounterBinding::GetCounterArray => "cairo_native__get_counters_array", } } @@ -56,7 +52,7 @@ impl LibfuncCounterBinding { match self { LibfuncCounterBinding::CounterId => std::ptr::null(), LibfuncCounterBinding::GetCounterArray => { - libfunc_counter_runtime::get_counter_array as *const () + libfunc_counter_runtime::get_counters_array as *const () } } } @@ -77,7 +73,7 @@ impl LibfuncCounterMeta { /// Register the global for the given binding, if not yet registered, and return /// a pointer to the stored value. /// - /// For the function to be available, `setup_runtime` must be called before running the module + /// For the function to be available, `setup_runtime` must be called before running the module. pub fn build_function<'c, 'a>( &mut self, context: &'c Context, @@ -151,8 +147,8 @@ impl LibfuncCounterMeta { block.append_op_result(memref::load(libfunc_counter_id_ptr, &[], location)) } - /// Build the array of counters - fn build_array_counter<'c, 'a>( + /// Returns the array of counters. + fn get_array_counter<'c, 'a>( &mut self, context: &'c Context, module: &Module, @@ -160,6 +156,7 @@ impl LibfuncCounterMeta { location: Location<'c>, ) -> Result> { self.build_counter_id(context, module, block, location)?; + let function_ptr = self.build_function( context, module, @@ -187,7 +184,7 @@ impl LibfuncCounterMeta { let u32_ty = IntegerType::new(context, 32).into(); let k1 = block.const_int(context, location, 1, 32)?; - let array_counter_ptr = self.build_array_counter(context, module, block, location)?; + let array_counter_ptr = self.get_array_counter(context, module, block, location)?; let value_counter_ptr = block.gep( context, @@ -233,9 +230,10 @@ pub mod libfunc_counter_runtime { use crate::{ error::Result, metadata::{libfunc_counter::LibfuncCounterMeta, MetadataStorage}, + utils::libc_malloc, }; - /// Contains an array of vector for each execution completed + /// Contains an array of vector for each execution completed. pub static LIBFUNC_COUNTER: LazyLock>>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -255,13 +253,13 @@ pub mod libfunc_counter_runtime { impl CountersArrayGuard { pub fn init(libfuncs_amount: usize) -> CountersArrayGuard { let u32_libfuncs_amount = libfuncs_amount * 4; + let new_array: *mut u32 = unsafe { libc_malloc(u32_libfuncs_amount).cast() }; - let new_array = unsafe { libc::malloc(u32_libfuncs_amount) as *mut u32 }; - + // All positions in the array must be initialized with 0. Since + // some libfuncs declared may not be called, their respective counter + // won't be updated. for i in 0..libfuncs_amount { - unsafe { - *(new_array.add(i)) = 0 - }; + unsafe { *(new_array.add(i)) = 0 }; } Self(COUNTERS_ARRAY.replace(new_array)) @@ -274,7 +272,7 @@ pub mod libfunc_counter_runtime { } } - /// Increase the libfunc's counter given its index + /// Update the libfunc's counter based on its index, relative to the order of declaration. pub fn count_libfunc( context: &Context, module: &Module, @@ -288,12 +286,15 @@ pub mod libfunc_counter_runtime { libfunc_counter.count_libfunc(context, module, block, location, libfunc_idx) } - pub extern "C" fn get_counter_array() -> *mut u32 { + pub extern "C" fn get_counters_array() -> *mut u32 { COUNTERS_ARRAY.with(|x| x.get()) as *mut u32 } + /// Converts the pointer to the counters into a Rust `Vec` and store it. Then, it frees the pointer. + /// + /// This method should be called at the end of an entrypoint execution. pub unsafe fn store_and_free_counters_array(counter_id_ptr: *mut u64, libfuncs_amount: usize) { - let counter_array_ptr = get_counter_array(); + let counter_array_ptr = get_counters_array(); let counters_vec = slice::from_raw_parts(counter_array_ptr, libfuncs_amount).to_vec(); LIBFUNC_COUNTER @@ -301,8 +302,6 @@ pub mod libfunc_counter_runtime { .unwrap() .insert(*counter_id_ptr, counters_vec); - if !(counter_array_ptr.is_null()) { - libc::free(counter_array_ptr as *mut libc::c_void); - } + libc::free(counter_array_ptr as *mut libc::c_void); } } From 5f31ef4894d91ffcaaae56b5ae5c672215ea1c64 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 17 Jul 2025 18:40:08 -0300 Subject: [PATCH 20/21] use malloc and free with trackers --- src/metadata/libfunc_counter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 65ba2ccf0..8c7e14490 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -230,7 +230,7 @@ pub mod libfunc_counter_runtime { use crate::{ error::Result, metadata::{libfunc_counter::LibfuncCounterMeta, MetadataStorage}, - utils::libc_malloc, + utils::{libc_free, libc_malloc}, }; /// Contains an array of vector for each execution completed. @@ -287,7 +287,7 @@ pub mod libfunc_counter_runtime { } pub extern "C" fn get_counters_array() -> *mut u32 { - COUNTERS_ARRAY.with(|x| x.get()) as *mut u32 + COUNTERS_ARRAY.with(|x| x.get()) } /// Converts the pointer to the counters into a Rust `Vec` and store it. Then, it frees the pointer. @@ -302,6 +302,6 @@ pub mod libfunc_counter_runtime { .unwrap() .insert(*counter_id_ptr, counters_vec); - libc::free(counter_array_ptr as *mut libc::c_void); + libc_free(counter_array_ptr as *mut libc::c_void); } } From 3ef6949bed7d9e0154c69b6fac24838c8cf05630 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 17 Jul 2025 18:49:46 -0300 Subject: [PATCH 21/21] fix typo --- src/metadata/libfunc_counter.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/metadata/libfunc_counter.rs b/src/metadata/libfunc_counter.rs index 8c7e14490..bbec51290 100644 --- a/src/metadata/libfunc_counter.rs +++ b/src/metadata/libfunc_counter.rs @@ -3,17 +3,17 @@ //! //! When this feature is used, the compiler will call one main method: //! -//! 1. `count_libfunc`: called before every libfunc execution. This method will handle the counting. Given a the index -//! of a libfunc (relative the its declaration order), it accesses the array of counters and updates the counter. +//! 1. `count_libfunc`: called before every libfunc execution. This method will handle the counting. Given the index +//! of a libfunc (relative to its declaration order), it accesses the array of counters and updates the counter. //! -//! In the context of Starknet contracts, we need to add support for building the arrays of counters for multiple executions. -//! To do so, we need one important element, which must be set before every contract execution: +//! In the context of Starknet contracts, we need to add support for building the array of counters for multiple executions. +//! To do so, we need one important element which must be set before every contract execution: //! //! * A counter to track the ID of the current array of counter, which gets updated every time we switch to another //! contract. Since a contract can call other contracts, we need a way of restoring the counter after every execution. //! -//! * An array-of-counter guard. Every time a new entrypoint is executed, a new array of counters needs to be created in order to isolate -//! each of them. The guard keeps the last array that was used to restore it once the inner entrypoint execution has finished. +//! * An array-of-counters guard. Every time a new entrypoint is executed, a new array of counters needs to be created. +//! The guard keeps the last array that was used to restore it once the inner entrypoint execution has finished. //! //! See `cairo-native-run` for an example on how to do it. use std::collections::HashSet;