Skip to content

Commit 5aa0110

Browse files
avi-starkwareclaude
andcommitted
blockifier,apollo_compile_to_native: adopt cairo_native::ContractExecutor + libfunc profiling
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3428d34 commit 5aa0110

11 files changed

Lines changed: 312 additions & 12 deletions

File tree

Cargo.lock

Lines changed: 105 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,11 @@ cairo-lang-sierra = "2.19.0-rc.3"
260260
cairo-lang-sierra-to-casm = "2.19.0-rc.3"
261261
cairo-lang-starknet-classes = "2.19.0-rc.3"
262262
cairo-lang-utils = "2.19.0-rc.3"
263-
cairo-native = "0.9.0-rc.7"
263+
# TEMP: git dep pinned to the tip of the unreleased PR stack
264+
# (starkware-libs/cairo_native#1610 → #1611 → #1612 → #1613) containing
265+
# `ContractExecutor`, `EmuContractInfo`, `AotWithProgram`, and `run_with_profile`.
266+
# Switch back to a crates.io version once those land in a published cairo-native release.
267+
cairo-native = { git = "https://github.com/starkware-libs/cairo_native.git", rev = "7107710f05801b63f3f40d863dd8ed38680cc6f6" }
264268
cairo-program-runner-lib = "1.1.0"
265269
cairo-vm = "3.2.0"
266270
camelpaste = "0.1.0"

crates/apollo_compile_to_native/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ description = "A utility crate for compiling Sierra code into Cairo native."
99
[lints]
1010
workspace = true
1111

12+
[features]
13+
# Enables `SierraToNativeCompiler::compile_with_program`, which returns the
14+
# AOT executor paired with the Sierra program required by cairo-native's
15+
# libfunc profiler.
16+
with-libfunc-profiling = ["cairo-native/with-libfunc-profiling"]
17+
1218
[dependencies]
1319
apollo_compilation_utils.workspace = true
1420
apollo_compile_to_native_types.workspace = true

crates/apollo_compile_to_native/src/compiler.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use std::path::PathBuf;
2+
#[cfg(feature = "with-libfunc-profiling")]
3+
use std::sync::Arc;
24

35
use apollo_compilation_utils::build_utils::verify_compiler_binary;
46
use apollo_compilation_utils::compiler_utils::compile_with_args;
@@ -8,6 +10,8 @@ use apollo_compilation_utils::resource_limits::ResourceLimits;
810
use apollo_compile_to_native_types::SierraCompilationConfig;
911
use cairo_lang_starknet_classes::contract_class::ContractClass;
1012
use cairo_native::executor::AotContractExecutor;
13+
#[cfg(feature = "with-libfunc-profiling")]
14+
use cairo_native::executor::AotWithProgram;
1115
use tempfile::NamedTempFile;
1216

1317
use crate::constants::{CAIRO_NATIVE_BINARY_NAME, REQUIRED_CAIRO_NATIVE_VERSION};
@@ -59,4 +63,25 @@ impl SierraToNativeCompiler {
5963
.map_err(|e| CompilationUtilError::CompilationError(e.to_string()))?
6064
.unwrap())
6165
}
66+
67+
/// Like [`Self::compile`], but also returns the Sierra program so cairo-native's
68+
/// libfunc profiler can resolve runtime libfunc IDs back to declarations. The
69+
/// program is extracted from `contract_class` before compilation so the two are
70+
/// guaranteed to correspond.
71+
#[cfg(feature = "with-libfunc-profiling")]
72+
pub fn compile_with_program(
73+
&self,
74+
contract_class: ContractClass,
75+
) -> Result<AotWithProgram, CompilationUtilError> {
76+
let program = contract_class
77+
.extract_sierra_program(false)
78+
.map(|extracted| Arc::new(extracted.program))
79+
.map_err(|err| {
80+
CompilationUtilError::UnexpectedError(format!(
81+
"Failed to extract Sierra program for profiling: {err}"
82+
))
83+
})?;
84+
let executor = self.compile(contract_class)?;
85+
Ok(AotWithProgram { executor, program })
86+
}
6287
}

crates/blockifier/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ node_api = []
2323
only-native = ["cairo_native"]
2424
os_input = []
2525
reexecution = ["transaction_serde"]
26+
# Enables the sierra-emu execution path of `cairo_native::ContractExecutor`. Pulls in
27+
# cairo-native's `sierra-emu` feature which compiles `SierraEmuSyscallBridge`.
28+
sierra-emu = ["cairo-native/sierra-emu", "cairo_native"]
2629
testing = [
2730
"blockifier_test_utils",
2831
"expect-test",
@@ -33,6 +36,15 @@ testing = [
3336
]
3437
tracing = []
3538
transaction_serde = []
39+
# Enables libfunc-level profiling for native execution. Pulls in cairo-native's
40+
# `with-libfunc-profiling` so `ContractExecutor::run_with_profile` is available,
41+
# and apollo_compile_to_native's matching feature so `SierraToNativeCompiler` can
42+
# pair the compiled executor with its Sierra program in one call.
43+
with-libfunc-profiling = [
44+
"apollo_compile_to_native/with-libfunc-profiling",
45+
"cairo-native/with-libfunc-profiling",
46+
"cairo_native",
47+
]
3648

3749
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
3850

crates/blockifier/src/execution/native.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
pub mod contract_class;
22
pub mod entry_point_execution;
3+
#[cfg(feature = "with-libfunc-profiling")]
4+
pub mod profiling;
5+
mod run_dispatch;
36
pub mod syscall_handler;
47
pub mod utils;
58

crates/blockifier/src/execution/native/contract_class.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ use std::borrow::Cow;
22
use std::ops::Deref;
33
use std::sync::Arc;
44

5-
use cairo_native::executor::AotContractExecutor;
5+
#[cfg(feature = "with-libfunc-profiling")]
6+
use cairo_native::executor::AotWithProgram;
7+
#[cfg(feature = "sierra-emu")]
8+
use cairo_native::executor::EmuContractInfo;
9+
use cairo_native::executor::{AotContractExecutor, ContractExecutor};
610
use starknet_api::contract_class::compiled_class_hash::HashableCompiledClass;
711
use starknet_api::core::EntryPointSelector;
812
use starknet_types_core::felt::Felt;
@@ -30,7 +34,28 @@ impl NativeCompiledClassV1 {
3034
/// executor must be derived from sierra_program which in turn must be derived from
3135
/// sierra_contract_class.
3236
pub fn new(executor: AotContractExecutor, casm: CompiledClassV1) -> NativeCompiledClassV1 {
33-
let contract = NativeCompiledClassV1Inner::new(executor, casm);
37+
let contract = NativeCompiledClassV1Inner::new(executor.into(), casm);
38+
39+
Self(Arc::new(contract))
40+
}
41+
42+
/// Initialize a compiled class backed by the sierra-emu interpreter instead of the AOT
43+
/// executor. Used by benchmarking / replay tooling that wants to execute through the emu
44+
/// VM while reusing the rest of the blockifier pipeline.
45+
#[cfg(feature = "sierra-emu")]
46+
pub fn new_from_emu(info: EmuContractInfo, casm: CompiledClassV1) -> NativeCompiledClassV1 {
47+
let contract = NativeCompiledClassV1Inner::new(info.into(), casm);
48+
49+
Self(Arc::new(contract))
50+
}
51+
52+
/// Like [`Self::new`], but also stores the Sierra `Program` (via the cairo-native
53+
/// [`AotWithProgram`] pairing) so [`cairo_native::ContractExecutor::run_with_profile`]
54+
/// can resolve libfunc samples. Only callable when the `with-libfunc-profiling`
55+
/// feature is enabled.
56+
#[cfg(feature = "with-libfunc-profiling")]
57+
pub fn new_with_program(info: AotWithProgram, casm: CompiledClassV1) -> NativeCompiledClassV1 {
58+
let contract = NativeCompiledClassV1Inner::new(info.into(), casm);
3459

3560
Self(Arc::new(contract))
3661
}
@@ -71,12 +96,12 @@ impl HashableCompiledClass<EntryPointV1, NestedFeltCounts> for NativeCompiledCla
7196

7297
#[derive(Debug)]
7398
pub struct NativeCompiledClassV1Inner {
74-
pub executor: AotContractExecutor,
99+
pub executor: ContractExecutor,
75100
casm: CompiledClassV1,
76101
}
77102

78103
impl NativeCompiledClassV1Inner {
79-
fn new(executor: AotContractExecutor, casm: CompiledClassV1) -> Self {
104+
fn new(executor: ContractExecutor, casm: CompiledClassV1) -> Self {
80105
NativeCompiledClassV1Inner { executor, casm }
81106
}
82107
}

crates/blockifier/src/execution/native/entry_point_execution.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::execution::contract_class::TrackedResource;
1414
use crate::execution::entry_point::{EntryPointExecutionContext, ExecutableCallEntryPoint};
1515
use crate::execution::errors::{EntryPointExecutionError, PostExecutionError, PreExecutionError};
1616
use crate::execution::native::contract_class::NativeCompiledClassV1;
17+
use crate::execution::native::run_dispatch::run_native_executor;
1718
use crate::execution::native::syscall_handler::NativeSyscallHandler;
1819
use crate::state::state_api::State;
1920
use crate::transaction::objects::ExecutionResourcesTraits;
@@ -57,11 +58,12 @@ pub fn execute_entry_point_call(
5758
.checked_sub(initial_budget)
5859
.ok_or(PreExecutionError::InsufficientEntryPointGas)?;
5960

60-
let execution_result = compiled_class.executor.run(
61+
let execution_result = run_native_executor(
62+
&compiled_class.executor,
6163
entry_point.selector.0,
6264
&syscall_handler.base.call.calldata.0.clone(),
6365
call_initial_gas,
64-
Some(builtin_costs),
66+
builtin_costs,
6567
&mut syscall_handler,
6668
);
6769

0 commit comments

Comments
 (0)