Skip to content

Commit e6a4ad9

Browse files
avi-starkwareclaude
andcommitted
add ContractExecutor dispatch enum (Aot + Emu)
Adds a public dispatch enum so a single call site can pick between the AOT executor and the sierra-emu interpreter at runtime, without forcing every caller to maintain its own match. - ContractExecutor { Aot(AotContractExecutor), Emu(EmuContractInfo) }, with Emu gated on the new sierra-emu cargo feature. - EmuContractInfo carries Arc<Program> so the program is shared across invocations rather than cloned per call. - ContractExecutor::run dispatches: Aot delegates to AotContractExecutor::run; Emu constructs a sierra_emu::VirtualMachine and runs it with the caller's syscall handler directly. No adapter is needed -- the trait is shared via the cairo-starknet-syscalls crate. VirtualMachine::run's `Option` return is propagated as Error::UnexpectedValue rather than `.expect()`-aborted, matching the Aot arm's error-handling style. - Cargo: new `sierra-emu` feature (= ["dep:sierra-emu"]). The existing `with-trace-dump` feature now activates `sierra-emu` instead of the optional dep directly. Companion to the bridge-free design: with cairo-native and sierra-emu sharing one trait, the SierraEmuSyscallBridge that PR #1597 / #1607 introduced is no longer necessary. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 487deb9 commit e6a4ad9

3 files changed

Lines changed: 135 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,10 @@ with-debug-utils = []
131131
with-mem-tracing = []
132132
with-libfunc-profiling = []
133133
with-segfault-catcher = []
134-
with-trace-dump = ["dep:sierra-emu"]
134+
with-trace-dump = ["sierra-emu"]
135+
# Enables the `ContractExecutor::Emu` variant for dispatching contract execution
136+
# through the sierra-emu interpreter.
137+
sierra-emu = ["dep:sierra-emu"]
135138
testing = ["dep:cairo-lang-compiler", "dep:cairo-lang-filesystem"]
136139

137140
[dependencies]

src/executor.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
//! This module provides methods to execute the programs, either via JIT or compiled ahead
44
//! of time. It also provides a cache to avoid recompiling previously compiled programs.
55
6-
pub use self::{aot::AotNativeExecutor, contract::AotContractExecutor, jit::JitNativeExecutor};
6+
#[cfg(feature = "sierra-emu")]
7+
pub use self::contract_executor::EmuContractInfo;
8+
pub use self::{
9+
aot::AotNativeExecutor, contract::AotContractExecutor, contract_executor::ContractExecutor,
10+
jit::JitNativeExecutor,
11+
};
712
use crate::{
813
arch::{AbiArgument, ValueWithInfoWrapper},
914
error::{panic::ToNativeAssertError, Error},
@@ -39,6 +44,7 @@ use std::{alloc::Layout, arch::global_asm, ptr::NonNull};
3944

4045
mod aot;
4146
mod contract;
47+
mod contract_executor;
4248
mod jit;
4349

4450
#[cfg(target_arch = "aarch64")]

src/executor/contract_executor.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//! Dispatch enum that lets a single call site choose between AOT-compiled execution and
2+
//! sierra-emu interpretation, without changing call-site code.
3+
//!
4+
//! The `Emu` variant is gated on the `sierra-emu` feature. Both variants share the same
5+
//! `H: StarknetSyscallHandler` -- sierra-emu and cairo-native re-export the trait from
6+
//! `cairo-native-syscalls`, so no adapter is needed.
7+
8+
#[cfg(feature = "sierra-emu")]
9+
use cairo_lang_sierra::program::Program;
10+
#[cfg(feature = "sierra-emu")]
11+
use cairo_lang_starknet_classes::compiler_version::VersionId;
12+
#[cfg(feature = "sierra-emu")]
13+
use cairo_lang_starknet_classes::contract_class::ContractEntryPoints;
14+
use starknet_types_core::felt::Felt;
15+
#[cfg(feature = "sierra-emu")]
16+
use std::sync::Arc;
17+
18+
#[cfg(feature = "sierra-emu")]
19+
use crate::error::Error;
20+
use crate::error::Result;
21+
use crate::execution_result::ContractExecutionResult;
22+
use crate::executor::AotContractExecutor;
23+
use crate::starknet::StarknetSyscallHandler;
24+
use crate::utils::BuiltinCosts;
25+
26+
/// Runtime selection between cairo-native's AOT executor and the sierra-emu interpreter.
27+
///
28+
/// `Emu` is constructed from the program + entry points + sierra version triple that
29+
/// `sierra_emu::VirtualMachine::new_starknet` requires; the `Arc<Program>` is shared across
30+
/// invocations rather than cloned per call.
31+
#[derive(Debug)]
32+
pub enum ContractExecutor {
33+
Aot(AotContractExecutor),
34+
#[cfg(feature = "sierra-emu")]
35+
Emu(EmuContractInfo),
36+
}
37+
38+
/// Inputs required to construct a `sierra_emu::VirtualMachine` for the `Emu` variant.
39+
#[cfg(feature = "sierra-emu")]
40+
#[derive(Debug, Clone)]
41+
pub struct EmuContractInfo {
42+
pub program: Arc<Program>,
43+
pub entry_points: ContractEntryPoints,
44+
pub sierra_version: VersionId,
45+
}
46+
47+
impl From<AotContractExecutor> for ContractExecutor {
48+
fn from(value: AotContractExecutor) -> Self {
49+
Self::Aot(value)
50+
}
51+
}
52+
53+
#[cfg(feature = "sierra-emu")]
54+
impl From<EmuContractInfo> for ContractExecutor {
55+
fn from(value: EmuContractInfo) -> Self {
56+
Self::Emu(value)
57+
}
58+
}
59+
60+
impl ContractExecutor {
61+
/// Run the contract entry point identified by `selector`.
62+
///
63+
/// Dispatches to [`AotContractExecutor::run`] for the `Aot` variant and to a
64+
/// [`sierra_emu::VirtualMachine`] for the `Emu` variant. The same `syscall_handler`
65+
/// flows through both paths unchanged -- its trait is shared across the two crates.
66+
pub fn run<H: StarknetSyscallHandler>(
67+
&self,
68+
selector: Felt,
69+
args: &[Felt],
70+
gas: u64,
71+
builtin_costs: Option<BuiltinCosts>,
72+
#[cfg_attr(not(feature = "sierra-emu"), allow(unused_mut))] mut syscall_handler: H,
73+
) -> Result<ContractExecutionResult> {
74+
match self {
75+
ContractExecutor::Aot(aot) => {
76+
aot.run(selector, args, gas, builtin_costs, syscall_handler)
77+
}
78+
#[cfg(feature = "sierra-emu")]
79+
ContractExecutor::Emu(EmuContractInfo {
80+
program,
81+
entry_points,
82+
sierra_version,
83+
}) => {
84+
let mut virtual_machine = sierra_emu::VirtualMachine::new_starknet(
85+
Arc::clone(program),
86+
entry_points,
87+
*sierra_version,
88+
);
89+
90+
let emu_builtin_costs = builtin_costs.map(convert_builtin_costs);
91+
92+
virtual_machine.call_contract(selector, gas, args.to_vec(), emu_builtin_costs);
93+
94+
// `VirtualMachine::run` returns `None` when the VM never produced a
95+
// final state -- propagate as an error rather than aborting the host.
96+
let result = virtual_machine.run(&mut syscall_handler).ok_or_else(|| {
97+
Error::UnexpectedValue("sierra-emu VM produced no final state".to_string())
98+
})?;
99+
100+
Ok(ContractExecutionResult {
101+
remaining_gas: result.remaining_gas,
102+
failure_flag: result.failure_flag,
103+
return_values: result.return_values,
104+
error_msg: result.error_msg,
105+
builtin_stats: Default::default(),
106+
})
107+
}
108+
}
109+
}
110+
}
111+
112+
#[cfg(feature = "sierra-emu")]
113+
fn convert_builtin_costs(builtin_costs: BuiltinCosts) -> sierra_emu::BuiltinCosts {
114+
sierra_emu::BuiltinCosts {
115+
r#const: builtin_costs.r#const,
116+
pedersen: builtin_costs.pedersen,
117+
bitwise: builtin_costs.bitwise,
118+
ecop: builtin_costs.ecop,
119+
poseidon: builtin_costs.poseidon,
120+
add_mod: builtin_costs.add_mod,
121+
mul_mod: builtin_costs.mul_mod,
122+
blake: builtin_costs.blake,
123+
}
124+
}

0 commit comments

Comments
 (0)