From 89789c65a104b694cd10800ec06e886ac9859d4d Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Wed, 28 Aug 2024 22:44:36 +0200 Subject: [PATCH 01/17] Add stuff. --- .github/workflows/ci.yml | 9 +- Cargo.lock | 32 +++- Cargo.toml | 1 + Makefile | 35 ++-- runtime/Cargo.toml | 14 +- runtime/src/lib.rs | 334 +++++++++++++++++++++++++++++++++++++ src/compiler.rs | 190 +++++++++++++++------ src/executor/aot.rs | 2 +- src/lib.rs | 3 + src/metadata.rs | 1 + src/metadata/trace_dump.rs | 162 ++++++++++++++++++ 11 files changed, 709 insertions(+), 74 deletions(-) create mode 100644 src/metadata/trace_dump.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7993bcb0..de7c905ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,12 +86,12 @@ jobs: MLIR_SYS_180_PREFIX: /usr/lib/llvm-18/ LLVM_SYS_181_PREFIX: /usr/lib/llvm-18/ TABLEGEN_180_PREFIX: /usr/lib/llvm-18/ - RUSTUP_TOOLCHAIN: nightly-2024-02-01 # udeps needs nightly + RUSTUP_TOOLCHAIN: nightly # udeps needs nightly steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2024-02-01 + toolchain: nightly components: rustfmt - name: add llvm deb repository @@ -253,7 +253,7 @@ jobs: with: scarb-version: "2.7.1" - name: Install deps - run: make deps + run: make deps - name: Build runtime and alexandria run: make runtime && make check-llvm && make needs-cairo2 && make build-alexandria - name: Build cairo-native-runtime library. @@ -262,7 +262,7 @@ jobs: - name: Run tests and generate coverage partition ${{ matrix.partition }} run: cargo llvm-cov nextest --verbose --all-features --workspace --lcov --output-path ${{ matrix.output }} --partition count:${{ matrix.partition }}/4 - - name: test and generate coverage corelib + - name: test and generate coverage corelib if: ${{ matrix.partition == '1' }} run: cargo llvm-cov nextest --verbose --all-features --lcov --output-path lcov-test.info run --bin cairo-native-test -- corelib @@ -282,7 +282,6 @@ jobs: name: coverage-data-${{ matrix.partition }} path: ./${{ matrix.output }} - upload-coverage: name: Upload Coverage runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 8f3d1d702..68df6019a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -934,9 +934,7 @@ dependencies = [ "bumpalo", "cairo-lang-compiler", "cairo-lang-defs", - "cairo-lang-diagnostics", "cairo-lang-filesystem", - "cairo-lang-lowering", "cairo-lang-runner", "cairo-lang-semantic", "cairo-lang-sierra", @@ -954,7 +952,6 @@ dependencies = [ "colored", "criterion", "educe", - "id-arena", "itertools 0.13.0", "k256", "keccak", @@ -992,10 +989,13 @@ dependencies = [ name = "cairo-native-runtime" version = "0.2.0" dependencies = [ + "cairo-lang-sierra", "cairo-lang-sierra-gas", + "cairo-lang-utils", "lazy_static", "libc", "rand", + "sierra-emu", "starknet-crypto 0.7.1", "starknet-curve 0.5.0", "starknet-types-core", @@ -3381,6 +3381,32 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "sierra-emu" +version = "0.1.0" +source = "git+https://github.com/lambdaclass/sierra-emu#5707a7b38763a4cab91e91c7230b56053d9a5d99" +dependencies = [ + "cairo-lang-sierra", + "cairo-lang-utils", + "clap", + "k256", + "keccak", + "num-bigint", + "num-traits 0.2.19", + "p256", + "rand", + "sec1", + "serde", + "serde_json", + "sha2", + "smallvec", + "starknet-crypto 0.7.1", + "starknet-curve 0.5.0", + "starknet-types-core", + "tracing", + "tracing-subscriber", +] + [[package]] name = "signature" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index dc6ac22a5..1b6f3a971 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ with-debug-utils = [] with-runtime = ["dep:cairo-native-runtime"] with-serde = ["dep:serde"] with-cheatcode = [] +with-trace-dump = ["cairo-native-runtime/with-trace-dump"] [dependencies] bumpalo = "3.16.0" diff --git a/Makefile b/Makefile index 361342e64..32a6a3349 100644 --- a/Makefile +++ b/Makefile @@ -40,48 +40,48 @@ usage: @echo " clean: Cleans the built artifacts." build: check-llvm runtime - cargo build --release --all-features + cargo build --release --features build-cli,with-cheatcode,with-runtime,with-serde build-native: check-llvm runtime - RUSTFLAGS="-C target-cpu=native" cargo build --release --all-features + RUSTFLAGS="-C target-cpu=native" cargo build --release --features build-cli,with-cheatcode,with-runtime,with-serde build-dev: check-llvm - cargo build --profile optimized-dev --all-features + cargo build --profile optimized-dev --features build-cli,with-cheatcode,with-runtime,with-serde check: check-llvm cargo fmt --all -- --check - cargo clippy --all-targets --all-features -- -D warnings + cargo clippy --all-targets --features build-cli,with-cheatcode,with-runtime,with-serde -- -D warnings test: check-llvm needs-cairo2 build-alexandria runtime-ci - cargo test --profile ci --all-features + cargo test --profile ci --features build-cli,with-cheatcode,with-runtime,with-serde test-cairo: check-llvm needs-cairo2 build-alexandria runtime-ci cargo r --profile ci --bin cairo-native-test -- corelib proptest: check-llvm needs-cairo2 runtime-ci - cargo test --profile ci --all-features proptest + cargo test --profile ci --features build-cli,with-cheatcode,with-runtime,with-serde proptest test-ci: check-llvm needs-cairo2 build-alexandria runtime-ci - cargo test --profile ci --all-features + cargo test --profile ci --features build-cli,with-cheatcode,with-runtime,with-serde proptest-ci: check-llvm needs-cairo2 runtime-ci - cargo test --profile ci --all-features proptest + cargo test --profile ci --features build-cli,with-cheatcode,with-runtime,with-serde proptest coverage: check-llvm needs-cairo2 build-alexandria runtime-ci - cargo llvm-cov --verbose --profile ci --all-features --workspace --lcov --output-path lcov.info - cargo llvm-cov --verbose --profile ci --all-features --lcov --output-path lcov-test.info run --bin cairo-native-test -- corelib + cargo llvm-cov --verbose --profile ci --features build-cli,with-cheatcode,with-runtime,with-serde --workspace --lcov --output-path lcov.info + cargo llvm-cov --verbose --profile ci --features build-cli,with-cheatcode,with-runtime,with-serde --lcov --output-path lcov-test.info run --bin cairo-native-test -- corelib doc: check-llvm - cargo doc --all-features --no-deps --workspace + cargo doc --features build-cli,with-cheatcode,with-runtime,with-serde --no-deps --workspace doc-open: check-llvm - cargo doc --all-features --no-deps --workspace --open + cargo doc --features build-cli,with-cheatcode,with-runtime,with-serde --no-deps --workspace --open bench: build needs-cairo2 runtime ./scripts/bench-hyperfine.sh bench-ci: check-llvm needs-cairo2 runtime - cargo criterion --all-features + cargo criterion --features build-cli,with-cheatcode,with-runtime,with-serde stress-test: check-llvm RUST_LOG=cairo_native_stress=DEBUG cargo run --bin cairo-native-stress 1000000 --output cairo-native-stress-logs.jsonl @@ -93,7 +93,7 @@ stress-clean: rm -rf .aot-cache install: check-llvm - RUSTFLAGS="-C target-cpu=native" cargo install --all-features --locked --path . + RUSTFLAGS="-C target-cpu=native" cargo install --features build-cli,with-cheatcode,with-runtime,with-serde --locked --path . clean: cargo clean @@ -148,7 +148,10 @@ build-alexandria: cd tests/alexandria; scarb build runtime: - cargo b --release --all-features -p cairo-native-runtime && cp target/release/libcairo_native_runtime.a . + cargo b --release -p cairo-native-runtime && cp target/release/libcairo_native_runtime.a . + +runtime-with-trace-dump: + cargo b --release -p cairo-native-runtime --features=with-trace-dump && cp target/release/libcairo_native_runtime.a . runtime-ci: - cargo b --profile ci --all-features -p cairo-native-runtime && cp target/ci/libcairo_native_runtime.a . + cargo b --profile ci -p cairo-native-runtime && cp target/ci/libcairo_native_runtime.a . diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 1c6a9f9dd..871b997ec 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,9 +8,17 @@ license = "Apache-2.0" [lib] crate-type = ["rlib", "cdylib", "staticlib"] +[features] +with-trace-dump = [ + "dep:cairo-lang-sierra", + "dep:cairo-lang-utils", + "dep:sierra-emu", +] + [dependencies] starknet-types-core = { version = "0.1.5", default-features = false, features = [ - "std", "serde", + "std", + "serde", ] } cairo-lang-sierra-gas = "2.7.1" libc = "0.2.158" @@ -18,3 +26,7 @@ starknet-crypto = "0.7.1" starknet-curve = "0.5.0" lazy_static = "1.5.0" rand = "0.8.5" + +cairo-lang-sierra = { version = "2.7.1", optional = true } +cairo-lang-utils = { version = "2.7.1", optional = true } +sierra-emu = { git = "https://github.com/lambdaclass/sierra-emu", optional = true } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 93ea7f81f..7dd975b07 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -472,3 +472,337 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_try_finalize_nz( true } } + +#[cfg(feature = "with-trace-dump")] +pub mod trace_dump { + use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + starknet::StarkNetTypeConcrete, + }, + ids::{ConcreteTypeId, VarId}, + program::StatementIdx, + program_registry::ProgramRegistry, + }; + use cairo_lang_utils::ordered_hash_map::OrderedHashMap; + use sierra_emu::{ProgramTrace, StateDump, Value}; + use starknet_crypto::Felt; + use std::{ + alloc::Layout, + collections::HashMap, + mem::swap, + ptr::NonNull, + sync::{LazyLock, Mutex}, + }; + + pub static TRACE_DUMP: LazyLock>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + + pub struct TraceDump { + pub trace: ProgramTrace, + state: OrderedHashMap, + registry: ProgramRegistry, + + get_layout: fn(&CoreTypeConcrete, &ProgramRegistry) -> Layout, + } + + impl TraceDump { + pub fn new( + registry: ProgramRegistry, + get_layout: fn(&CoreTypeConcrete, &ProgramRegistry) -> Layout, + ) -> Self { + Self { + trace: ProgramTrace::default(), + state: OrderedHashMap::default(), + registry, + + get_layout, + } + } + } + + #[no_mangle] + pub extern "C" fn get_trace_dump_ptr() -> *const Mutex> { + &*TRACE_DUMP as *const _ + } + + #[no_mangle] + pub unsafe extern "C" fn cairo_native__trace_dump__state( + trace_id: u64, + var_id: u64, + type_id: u64, + value_ptr: NonNull<()>, + ) { + let mut trace_dump = TRACE_DUMP.lock().unwrap(); + let trace_dump = trace_dump.get_mut(&trace_id).unwrap(); + + let type_id = ConcreteTypeId::new(type_id); + let value = read_value_ptr( + &trace_dump.registry, + &type_id, + value_ptr, + trace_dump.get_layout, + ); + + trace_dump.state.insert(VarId::new(var_id), value); + } + + #[no_mangle] + pub unsafe extern "C" fn cairo_native__trace_dump__push(trace_id: u64, statement_idx: u64) { + let mut trace_dump = TRACE_DUMP.lock().unwrap(); + let trace_dump = trace_dump.get_mut(&trace_id).unwrap(); + + let mut items = OrderedHashMap::default(); + swap(&mut items, &mut trace_dump.state); + + trace_dump + .trace + .push(StateDump::new(StatementIdx(statement_idx as usize), items)); + } + + unsafe fn read_value_ptr( + registry: &ProgramRegistry, + type_id: &ConcreteTypeId, + value_ptr: NonNull<()>, + get_layout: fn(&CoreTypeConcrete, &ProgramRegistry) -> Layout, + ) -> Value { + let type_info = registry.get_type(type_id).unwrap(); + match type_info { + CoreTypeConcrete::Felt252(_) + | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::ContractAddress(_)) + | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::StorageAddress(_)) + | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::StorageBaseAddress(_)) => { + Value::Felt(Felt::from_bytes_le(value_ptr.cast().as_ref())) + } + CoreTypeConcrete::Uint8(_) => Value::U8(value_ptr.cast().read()), + CoreTypeConcrete::Uint16(_) => Value::U16(value_ptr.cast().read()), + CoreTypeConcrete::Uint32(_) => Value::U32(value_ptr.cast().read()), + CoreTypeConcrete::Uint64(_) => Value::U64(value_ptr.cast().read()), + CoreTypeConcrete::Uint128(_) | CoreTypeConcrete::GasBuiltin(_) => { + Value::U128(value_ptr.cast().read()) + } + + CoreTypeConcrete::EcPoint(_) => { + let layout = Layout::new::<()>(); + let (x, layout) = { + let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); + ( + Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + layout, + ) + }; + let (y, _) = { + let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); + ( + Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + layout, + ) + }; + + Value::EcPoint { x, y } + } + CoreTypeConcrete::EcState(_) => { + let layout = Layout::new::<()>(); + let (x0, layout) = { + let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); + ( + Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + layout, + ) + }; + let (y0, layout) = { + let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); + ( + Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + layout, + ) + }; + let (x1, layout) = { + let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); + ( + Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + layout, + ) + }; + let (y1, _) = { + let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); + ( + Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + layout, + ) + }; + + Value::EcState { x0, y0, x1, y1 } + } + + CoreTypeConcrete::Uninitialized(info) => Value::Uninitialized { + ty: info.ty.clone(), + }, + CoreTypeConcrete::Box(info) => read_value_ptr( + registry, + &info.ty, + value_ptr.cast::>().read(), + get_layout, + ), + CoreTypeConcrete::Array(info) => { + let layout = Layout::new::<()>(); + let (array_ptr, layout) = { + let (layout, offset) = layout.extend(Layout::new::<*mut ()>()).unwrap(); + (value_ptr.byte_add(offset).cast::<*mut ()>().read(), layout) + }; + let (array_begin, layout) = { + let (layout, offset) = layout.extend(Layout::new::()).unwrap(); + (value_ptr.byte_add(offset).cast::().read(), layout) + }; + let (array_end, _) = { + let (layout, offset) = layout.extend(Layout::new::()).unwrap(); + (value_ptr.byte_add(offset).cast::().read(), layout) + }; + + let layout = + get_layout(registry.get_type(&info.ty).unwrap(), registry).pad_to_align(); + + let mut data = Vec::with_capacity((array_end - array_begin) as usize); + for index in array_begin..array_end { + let index = index as usize; + + data.push(read_value_ptr( + registry, + &info.ty, + NonNull::new(array_ptr.byte_add(layout.size() * index)).unwrap(), + get_layout, + )); + } + + Value::Array { + ty: info.ty.clone(), + data, + } + } + + CoreTypeConcrete::Struct(info) => { + let mut layout = Layout::new::<()>(); + let mut members = Vec::with_capacity(info.members.len()); + for member_ty in &info.members { + let type_info = registry.get_type(member_ty).unwrap(); + let member_layout = get_layout(type_info, registry); + + let offset; + (layout, offset) = layout.extend(member_layout).unwrap(); + + let current_ptr = value_ptr.byte_add(offset); + members.push(read_value_ptr(registry, member_ty, current_ptr, get_layout)); + } + + Value::Struct(members) + } + CoreTypeConcrete::Enum(info) => { + let num_variants = match info.variants.len() { + 0 => unreachable!(), + 1 => 0, + n => (n.next_power_of_two().next_multiple_of(8) >> 3) as _, + }; + + let layout = Layout::new::<()>(); + let (tag_value, layout) = match num_variants { + x if x <= 8 => { + let (layout, offset) = layout.extend(Layout::new::()).unwrap(); + ( + value_ptr.byte_add(offset).cast::().read() as usize, + layout, + ) + } + x if x <= 16 => { + let (layout, offset) = layout.extend(Layout::new::()).unwrap(); + ( + value_ptr.byte_add(offset).cast::().read() as usize, + layout, + ) + } + x if x <= 32 => { + let (layout, offset) = layout.extend(Layout::new::()).unwrap(); + ( + value_ptr.byte_add(offset).cast::().read() as usize, + layout, + ) + } + x if x <= 64 => { + let (layout, offset) = layout.extend(Layout::new::()).unwrap(); + ( + value_ptr.byte_add(offset).cast::().read() as usize, + layout, + ) + } + _ => todo!(), + }; + + let payload = { + let (_, offset) = layout + .extend(get_layout( + registry.get_type(&info.variants[tag_value]).unwrap(), + registry, + )) + .unwrap(); + + read_value_ptr( + registry, + &info.variants[tag_value], + value_ptr.byte_add(offset), + get_layout, + ) + }; + + Value::Enum { + self_ty: type_id.clone(), + index: tag_value, + payload: Box::new(payload), + } + } + + CoreTypeConcrete::NonZero(info) | CoreTypeConcrete::Snapshot(info) => { + read_value_ptr(registry, &info.ty, value_ptr, get_layout) + } + + // Builtins and other unit types: + CoreTypeConcrete::Bitwise(_) + | CoreTypeConcrete::BuiltinCosts(_) + | CoreTypeConcrete::EcOp(_) + | CoreTypeConcrete::Pedersen(_) + | CoreTypeConcrete::Poseidon(_) + | CoreTypeConcrete::RangeCheck(_) + | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::System(_)) + | CoreTypeConcrete::Uint128MulGuarantee(_) => Value::Unit, + + // TODO: + CoreTypeConcrete::Coupon(_) => todo!("CoreTypeConcrete::Coupon"), + CoreTypeConcrete::Circuit(_) => todo!("CoreTypeConcrete::Circuit"), + CoreTypeConcrete::Const(_) => todo!("CoreTypeConcrete::Const"), + CoreTypeConcrete::Sint8(_) => todo!("CoreTypeConcrete::Sint8"), + CoreTypeConcrete::Sint16(_) => todo!("CoreTypeConcrete::Sint16"), + CoreTypeConcrete::Sint32(_) => todo!("CoreTypeConcrete::Sint32"), + CoreTypeConcrete::Sint64(_) => todo!("CoreTypeConcrete::Sint64"), + CoreTypeConcrete::Sint128(_) => todo!("CoreTypeConcrete::Sint128"), + CoreTypeConcrete::Nullable(_) => todo!("CoreTypeConcrete::Nullable"), + CoreTypeConcrete::RangeCheck96(_) => todo!("CoreTypeConcrete::RangeCheck96"), + CoreTypeConcrete::Felt252Dict(_) => todo!("CoreTypeConcrete::Felt252Dict"), + CoreTypeConcrete::Felt252DictEntry(_) => todo!("CoreTypeConcrete::Felt252DictEntry"), + CoreTypeConcrete::SquashedFelt252Dict(_) => { + todo!("CoreTypeConcrete::SquashedFelt252Dict") + } + CoreTypeConcrete::Span(_) => todo!("CoreTypeConcrete::Span"), + CoreTypeConcrete::StarkNet(selector) => match selector { + StarkNetTypeConcrete::ClassHash(_) => todo!("StarkNetTypeConcrete::ClassHash"), + StarkNetTypeConcrete::Secp256Point(_) => { + todo!("StarkNetTypeConcrete::Secp256Point") + } + StarkNetTypeConcrete::Sha256StateHandle(_) => { + todo!("StarkNetTypeConcrete::Sha256StateHandle") + } + _ => unreachable!(), + }, + CoreTypeConcrete::SegmentArena(_) => todo!("CoreTypeConcrete::SegmentArena"), + CoreTypeConcrete::Bytes31(_) => todo!("CoreTypeConcrete::Bytes31"), + CoreTypeConcrete::BoundedInt(_) => todo!("CoreTypeConcrete::BoundedInt"), + } + } +} diff --git a/src/compiler.rs b/src/compiler.rs index 61b399569..e66535f83 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -356,40 +356,45 @@ fn compile_func( ), ); - let initial_state = edit_state::put_results(OrderedHashMap::<_, Value>::default(), { - let mut values = Vec::new(); - - let mut count = 0; - for param in &function.params { - let type_info = registry.get_type(¶m.ty)?; - let location = Location::new( - context, - "program.sierra", - sierra_stmt_start_offset + function.entry_point.0, - 0, - ); + let initial_state = + edit_state::put_results(OrderedHashMap::<_, (&ConcreteTypeId, Value)>::default(), { + let mut values = Vec::new(); + + let mut count = 0; + for param in &function.params { + let type_info = registry.get_type(¶m.ty)?; + let location = Location::new( + context, + "program.sierra", + sierra_stmt_start_offset + function.entry_point.0, + 0, + ); - values.push(( - ¶m.id, - if type_info.is_builtin() && type_info.is_zst(registry) { - pre_entry_block - .append_operation(llvm::undef( - type_info.build(context, module, registry, metadata, ¶m.ty)?, - location, - )) - .result(0)? - .into() - } else { - let value = entry_block.argument(count)?.into(); - count += 1; + values.push(( + ¶m.id, + ( + ¶m.ty, + if type_info.is_builtin() && type_info.is_zst(registry) { + pre_entry_block + .append_operation(llvm::undef( + type_info + .build(context, module, registry, metadata, ¶m.ty)?, + location, + )) + .result(0)? + .into() + } else { + let value = entry_block.argument(count)?.into(); + count += 1; - value - }, - )); - } + value + }, + ), + )); + } - values.into_iter() - })?; + values.into_iter() + })?; tracing::trace!("Implementing the entry block."); entry_block.append_operation(cf::br( @@ -399,7 +404,7 @@ fn compile_func( Statement::Return(x) => x, } .iter() - .map(|x| initial_state[x]) + .map(|x| initial_state[x].1) .collect::>(), { Location::new( @@ -431,10 +436,12 @@ fn compile_func( state = edit_state::put_results( OrderedHashMap::default(), state - .keys() - .sorted_by_key(|x| x.id) + .iter() + .sorted_by_key(|(x, _)| x.id) .enumerate() - .map(|(idx, var_id)| Ok((var_id, landing_block.argument(idx)?.into()))) + .map(|(idx, (var_id, (ty, _)))| { + Ok((var_id, (*ty, landing_block.argument(idx)?.into()))) + }) .collect::, Error>>()? .into_iter(), )?; @@ -449,7 +456,10 @@ fn compile_func( } .iter(), )? - .1, + .1 + .iter() + .map(|x| x.1) + .collect::>(), Location::name( context, &format!("landing_block(stmt_idx={})", statement_idx), @@ -493,6 +503,21 @@ fn compile_func( format!("{}(stmt_idx={})", libfunc_to_name(libf), statement_idx) }; + #[cfg(feature = "with-trace-dump")] + self::trace_dump::build_state_snapshot( + metadata.get_or_insert_with( + crate::metadata::trace_dump::TraceDumpMeta::default, + ), + context, + registry, + module, + &pre_entry_block, + block, + Location::unknown(context), + statement_idx, + &state, + ); + let (state, _) = edit_state::take_args(state, invocation.args.iter())?; let helper = LibfuncHelper { @@ -581,8 +606,9 @@ fn compile_func( invocation .branches .iter() + .zip(concrete_libfunc.branch_signatures()) .zip(helper.results()) - .map(|(branch_info, result_values)| { + .map(|((branch_info, signature), result_values)| { assert_eq!( branch_info.results.len(), result_values.len(), @@ -592,10 +618,13 @@ fn compile_func( Ok(( edit_state::put_results( state.clone(), - branch_info - .results - .iter() - .zip(result_values.iter().copied()), + branch_info.results.iter().zip( + signature + .vars + .iter() + .map(|x| &x.ty) + .zip(result_values.iter().copied()), + ), )?, tailrec_state.clone(), )) @@ -616,6 +645,21 @@ fn compile_func( ), ); + #[cfg(feature = "with-trace-dump")] + self::trace_dump::build_state_snapshot( + metadata.get_or_insert_with( + crate::metadata::trace_dump::TraceDumpMeta::default, + ), + context, + registry, + module, + &pre_entry_block, + block, + Location::unknown(context), + statement_idx, + &state, + ); + let (_, mut values) = edit_state::take_args(state, var_ids.iter())?; let mut block = *block; @@ -683,7 +727,7 @@ fn compile_func( .ret_types .iter() .zip(&values) - .filter_map(|(type_id, value)| { + .filter_map(|(type_id, (_, value))| { let type_info = registry.get_type(type_id).unwrap(); if type_info.is_zst(registry) || type_info.is_memory_allocated(registry) @@ -699,7 +743,7 @@ fn compile_func( .ret_types .iter() .zip(&values) - .filter_map(|(type_id, value)| { + .filter_map(|(type_id, (_, value))| { let type_info = registry.get_type(type_id).unwrap(); if type_info.is_zst(registry) { None @@ -738,7 +782,7 @@ fn compile_func( let (_ret_type_id, ret_type_info) = return_type_infos[0]; let ret_layout = ret_type_info.layout(registry)?; - let ptr = values.remove(0); + let (_, ptr) = values.remove(0); block.append_operation(llvm::store( context, ptr, @@ -751,7 +795,10 @@ fn compile_func( )); } - block.append_operation(func::r#return(&values, location)); + block.append_operation(func::r#return( + &values.into_iter().map(|x| x.1).collect::>(), + location, + )); Vec::new() } @@ -1130,7 +1177,7 @@ fn generate_branching_targets<'ctx, 'this, 'a>( statements: &'this [Statement], statement_idx: StatementIdx, invocation: &'this Invocation, - state: &OrderedHashMap>, + state: &OrderedHashMap)>, ) -> Vec<(&'this Block<'ctx>, Vec>)> where 'this: 'ctx, @@ -1149,7 +1196,7 @@ where .map(|var_id| { match branch.results.iter().find_position(|id| *id == var_id) { Some((i, _)) => BranchArg::Returned(i), - None => BranchArg::External(state[var_id]), + None => BranchArg::External(state[var_id].1), } }) .collect::>(); @@ -1170,7 +1217,7 @@ where .find_map(|(i, id)| (id == var_id).then_some(i)) { Some(i) => BranchArg::Returned(i), - None => BranchArg::External(state[var_id]), + None => BranchArg::External(state[var_id].1), } }) .collect::>(); @@ -1181,3 +1228,50 @@ where }) .collect() } + +#[cfg(feature = "with-trace-dump")] +mod trace_dump { + use crate::{block_ext::BlockExt, metadata::trace_dump::TraceDumpMeta, types::TypeBuilder}; + use cairo_lang_sierra::{ + extensions::core::{CoreLibfunc, CoreType}, + ids::{ConcreteTypeId, VarId}, + program::StatementIdx, + program_registry::ProgramRegistry, + }; + use cairo_lang_utils::ordered_hash_map::OrderedHashMap; + use melior::{ + ir::{BlockRef, Location, Module, Value, ValueLike}, + Context, + }; + + #[allow(clippy::too_many_arguments)] + pub fn build_state_snapshot( + trace_dump: &mut TraceDumpMeta, + context: &Context, + registry: &ProgramRegistry, + module: &Module, + init_block: &BlockRef, + block: &BlockRef, + location: Location, + statement_idx: StatementIdx, + state: &OrderedHashMap, + ) { + for (var_id, (type_id, value)) in state.iter() { + let type_info = registry.get_type(type_id).unwrap(); + let layout = type_info.layout(registry).unwrap(); + + let ptr_value = init_block + .alloca1(context, location, value.r#type(), layout.align()) + .unwrap(); + block.store(context, location, ptr_value, *value).unwrap(); + + trace_dump + .build_state(context, module, block, var_id, type_id, ptr_value, location) + .unwrap(); + } + + trace_dump + .build_push(context, module, block, statement_idx, location) + .unwrap(); + } +} diff --git a/src/executor/aot.rs b/src/executor/aot.rs index de9b663df..f14c40891 100644 --- a/src/executor/aot.rs +++ b/src/executor/aot.rs @@ -24,7 +24,7 @@ use tempfile::NamedTempFile; #[educe(Debug)] pub struct AotNativeExecutor { #[educe(Debug(ignore))] - library: Library, + pub library: Library, #[educe(Debug(ignore))] registry: ProgramRegistry, diff --git a/src/lib.rs b/src/lib.rs index 8367af7e1..843095c18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,9 @@ pub use self::{ ffi::{module_to_object, object_to_shared_lib, LLVMCompileError, OptLevel}, }; +#[cfg(feature = "with-trace-dump")] +pub use cairo_native_runtime as runtime; + mod arch; pub(crate) mod block_ext; pub mod cache; diff --git a/src/metadata.rs b/src/metadata.rs index a09f4f591..37aea97c6 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -23,6 +23,7 @@ pub mod realloc_bindings; pub mod runtime_bindings; pub mod snapshot_clones; pub mod tail_recursion; +pub mod trace_dump; /// Metadata container. #[cfg_attr(not(feature = "with-debug-utils"), derive(Default))] diff --git a/src/metadata/trace_dump.rs b/src/metadata/trace_dump.rs new file mode 100644 index 000000000..d4912dd08 --- /dev/null +++ b/src/metadata/trace_dump.rs @@ -0,0 +1,162 @@ +#![cfg(feature = "with-trace-dump")] + +use crate::{block_ext::BlockExt, error::Result}; +use cairo_lang_sierra::{ + ids::{ConcreteTypeId, VarId}, + program::StatementIdx, +}; +use melior::{ + dialect::{func, llvm, memref}, + ir::{ + attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, + r#type::{FunctionType, IntegerType, MemRefType}, + Block, Identifier, Location, Module, Region, Value, + }, + Context, +}; +use std::collections::HashSet; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +enum TraceBinding { + State, + Push, + TraceId, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct TraceDumpMeta { + bindings: HashSet, +} + +impl TraceDumpMeta { + #[allow(clippy::too_many_arguments)] + pub fn build_state( + &mut self, + context: &Context, + module: &Module, + block: &Block, + var_id: &VarId, + value_ty: &ConcreteTypeId, + value_ptr: Value, + location: Location, + ) -> Result<()> { + self.build_trace_id(context, module)?; + if self.bindings.insert(TraceBinding::State) { + module.body().append_operation(func::func( + context, + StringAttribute::new(context, "cairo_native__trace_dump__state"), + TypeAttribute::new( + FunctionType::new( + context, + &[ + IntegerType::new(context, 64).into(), // Trace ID. + IntegerType::new(context, 64).into(), // Var ID. + IntegerType::new(context, 64).into(), // Value type (`ConcreteTypeId`). + llvm::r#type::pointer(context, 0), // Value pointer. + ], + &[], + ) + .into(), + ), + Region::new(), + &[( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + )], + location, + )); + } + + let trace_id = block + .append_op_result(memref::get_global( + context, + "TRACE_DUMP__TRACE_ID", + MemRefType::new(IntegerType::new(context, 64).into(), &[], None, None), + location, + )) + .unwrap(); + let trace_id = block.append_op_result(memref::load(trace_id, &[], location))?; + let var_id = block.const_int(context, location, var_id.id, 64)?; + let value_ty = block.const_int(context, location, value_ty.id, 64)?; + block.append_operation(func::call( + context, + FlatSymbolRefAttribute::new(context, "cairo_native__trace_dump__state"), + &[trace_id, var_id, value_ty, value_ptr], + &[], + location, + )); + + Ok(()) + } + + pub fn build_push( + &mut self, + context: &Context, + module: &Module, + block: &Block, + statement_idx: StatementIdx, + location: Location, + ) -> Result<()> { + self.build_trace_id(context, module)?; + if self.bindings.insert(TraceBinding::Push) { + module.body().append_operation(func::func( + context, + StringAttribute::new(context, "cairo_native__trace_dump__push"), + TypeAttribute::new( + FunctionType::new( + context, + &[ + IntegerType::new(context, 64).into(), // Trace ID. + IntegerType::new(context, 64).into(), // Statement index. + ], + &[], + ) + .into(), + ), + Region::new(), + &[( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + )], + Location::unknown(context), + )); + } + + let trace_id = block + .append_op_result(memref::get_global( + context, + "TRACE_DUMP__TRACE_ID", + MemRefType::new(IntegerType::new(context, 64).into(), &[], None, None), + location, + )) + .unwrap(); + let trace_id = block.append_op_result(memref::load(trace_id, &[], location))?; + let statement_idx = block.const_int(context, location, statement_idx.0, 64)?; + block.append_operation(func::call( + context, + FlatSymbolRefAttribute::new(context, "cairo_native__trace_dump__push"), + &[trace_id, statement_idx], + &[], + location, + )); + + Ok(()) + } + + fn build_trace_id(&mut self, context: &Context, module: &Module) -> Result<()> { + if self.bindings.insert(TraceBinding::TraceId) { + module.body().append_operation(memref::global( + context, + "TRACE_DUMP__TRACE_ID", + None, + MemRefType::new(IntegerType::new(context, 64).into(), &[], None, None), + None, + false, + None, + Location::unknown(context), + )); + } + + Ok(()) + } +} From 1aaaea894e612f7777fd01094e5ed6f787fadf43 Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Wed, 28 Aug 2024 23:05:56 +0200 Subject: [PATCH 02/17] Fix bug. --- runtime/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 7dd975b07..883abdce2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -42,7 +42,7 @@ pub unsafe extern "C" fn cairo_native__libfunc__debug__print( let data = *data.add(i); let value = Felt::from_bytes_le(&data); - if write!(target, "[DEBUG]\t{value:x}",).is_err() { + if write!(target, "[DEBUG]\t0x{value:x}",).is_err() { return 1; }; @@ -587,14 +587,14 @@ pub mod trace_dump { let (x, layout) = { let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); ( - Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + Felt::from_bytes_le(value_ptr.byte_add(offset).cast().as_ref()), layout, ) }; let (y, _) = { let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); ( - Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + Felt::from_bytes_le(value_ptr.byte_add(offset).cast().as_ref()), layout, ) }; @@ -606,28 +606,28 @@ pub mod trace_dump { let (x0, layout) = { let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); ( - Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + Felt::from_bytes_le(value_ptr.byte_add(offset).cast().as_ref()), layout, ) }; let (y0, layout) = { let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); ( - Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + Felt::from_bytes_le(value_ptr.byte_add(offset).cast().as_ref()), layout, ) }; let (x1, layout) = { let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); ( - Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + Felt::from_bytes_le(value_ptr.byte_add(offset).cast().as_ref()), layout, ) }; let (y1, _) = { let (layout, offset) = layout.extend(Layout::new::<[u128; 2]>()).unwrap(); ( - Felt::from_bytes_be(value_ptr.byte_add(offset).cast().as_ref()), + Felt::from_bytes_le(value_ptr.byte_add(offset).cast().as_ref()), layout, ) }; From 12e3d8eb6f8e0c68aea86ba21e6143384bdbafb3 Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Tue, 10 Sep 2024 19:16:58 +0200 Subject: [PATCH 03/17] Fix stuff. --- Cargo.toml | 2 +- src/bin/cairo-native-run.rs | 40 ++++++++++++- src/compiler.rs | 111 ++++++++++++++++++++---------------- src/metadata/trace_dump.rs | 22 ++++++- src/utils.rs | 16 ++++-- 5 files changed, 133 insertions(+), 58 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f03155966..48e97d088 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ with-debug-utils = [] with-runtime = ["dep:cairo-native-runtime"] with-serde = ["dep:serde"] with-cheatcode = [] -with-trace-dump = ["cairo-native-runtime/with-trace-dump"] +with-trace-dump = ["dep:serde_json", "cairo-native-runtime/with-trace-dump"] # the aquamarine dep is only used in docs and cannot be detected as used by cargo udeps [package.metadata.cargo-udeps.ignore] diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index 2b51ab62a..4443b29ad 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -16,7 +16,7 @@ use std::path::PathBuf; use tracing_subscriber::{EnvFilter, FmtSubscriber}; use utils::{find_function, result_to_runresult}; -#[derive(Clone, Debug, ValueEnum)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] enum RunMode { Aot, Jit, @@ -44,6 +44,10 @@ struct Args { /// Optimization level, Valid: 0, 1, 2, 3. Values higher than 3 are considered as 3. #[arg(short = 'O', long, default_value_t = 0)] opt_level: u8, + + #[cfg(feature = "with-trace-dump")] + #[arg(long)] + trace_output: Option, } fn main() -> anyhow::Result<()> { @@ -83,6 +87,21 @@ fn main() -> anyhow::Result<()> { } }; + #[cfg(feature = "with-trace-dump")] + { + use cairo_lang_sierra::program_registry::ProgramRegistry; + use cairo_native::types::TypeBuilder; + use cairo_native_runtime::trace_dump::{TraceDump, TRACE_DUMP}; + + TRACE_DUMP.lock().unwrap().insert( + 0, + TraceDump::new( + ProgramRegistry::new(&sierra_program).unwrap(), + |ty, registry| ty.layout(registry).unwrap(), + ), + ); + } + let gas_metadata = GasMetadata::new(&sierra_program, Some(MetadataComputationConfig::default())).unwrap(); @@ -119,5 +138,24 @@ fn main() -> anyhow::Result<()> { println!("Remaining gas: {gas}"); } + #[cfg(feature = "with-trace-dump")] + if let Some(trace_output) = args.trace_output { + assert_eq!( + args.run_mode, + RunMode::Jit, + "AOT trace dump for programs is not yet supported" + ); + + let traces = cairo_native_runtime::trace_dump::TRACE_DUMP.lock().unwrap(); + assert_eq!(traces.len(), 1); + + let trace_dump = traces.values().next().unwrap(); + serde_json::to_writer( + std::fs::File::create(trace_output).unwrap(), + &trace_dump.trace, + ) + .unwrap(); + } + Ok(()) } diff --git a/src/compiler.rs b/src/compiler.rs index c40028de2..10188a67b 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -356,40 +356,45 @@ fn compile_func( let pre_entry_block = region.insert_block_before(entry_block, Block::new(&pre_entry_block_args)); - let initial_state = edit_state::put_results(OrderedHashMap::<_, Value>::default(), { - let mut values = Vec::new(); - - let mut count = 0; - for param in &function.params { - let type_info = registry.get_type(¶m.ty)?; - let location = Location::new( - context, - "program.sierra", - sierra_stmt_start_offset + function.entry_point.0, - 0, - ); + let initial_state = + edit_state::put_results(OrderedHashMap::<_, (&ConcreteTypeId, Value)>::default(), { + let mut values = Vec::new(); + + let mut count = 0; + for param in &function.params { + let type_info = registry.get_type(¶m.ty)?; + let location = Location::new( + context, + "program.sierra", + sierra_stmt_start_offset + function.entry_point.0, + 0, + ); - values.push(( - ¶m.id, - if type_info.is_builtin() && type_info.is_zst(registry) { - pre_entry_block - .append_operation(llvm::undef( - type_info.build(context, module, registry, metadata, ¶m.ty)?, - location, - )) - .result(0)? - .into() - } else { - let value = entry_block.argument(count)?.into(); - count += 1; + values.push(( + ¶m.id, + ( + ¶m.ty, + if type_info.is_builtin() && type_info.is_zst(registry) { + pre_entry_block + .append_operation(llvm::undef( + type_info + .build(context, module, registry, metadata, ¶m.ty)?, + location, + )) + .result(0)? + .into() + } else { + let value = entry_block.argument(count)?.into(); + count += 1; - value - }, - )); - } + value + }, + ), + )); + } - values.into_iter() - })?; + values.into_iter() + })?; tracing::trace!("Implementing the entry block."); entry_block.append_operation(cf::br( @@ -399,7 +404,7 @@ fn compile_func( Statement::Return(x) => x, } .iter() - .map(|x| initial_state[x]) + .map(|x| initial_state[x].1) .collect::>(), { Location::new( @@ -431,10 +436,12 @@ fn compile_func( state = edit_state::put_results( OrderedHashMap::default(), state - .keys() - .sorted_by_key(|x| x.id) + .iter() + .sorted_by_key(|(x, _)| x.id) .enumerate() - .map(|(idx, var_id)| Ok((var_id, landing_block.argument(idx)?.into()))) + .map(|(idx, (var_id, (ty, _)))| { + Ok((var_id, (*ty, landing_block.argument(idx)?.into()))) + }) .collect::, Error>>()? .into_iter(), )?; @@ -449,7 +456,10 @@ fn compile_func( } .iter(), )? - .1, + .1 + .iter() + .map(|x| x.1) + .collect::>(), Location::name( context, &format!("landing_block(stmt_idx={})", statement_idx), @@ -597,8 +607,9 @@ fn compile_func( invocation .branches .iter() + .zip(libfunc.branch_signatures()) .zip(helper.results()) - .map(|(branch_info, result_values)| { + .map(|((branch_info, signature), result_values)| { assert_eq!( branch_info.results.len(), result_values.len(), @@ -607,10 +618,13 @@ fn compile_func( Ok(edit_state::put_results( state.clone(), - branch_info - .results - .iter() - .zip(result_values.iter().copied()), + branch_info.results.iter().zip( + signature + .vars + .iter() + .map(|x| &x.ty) + .zip(result_values.iter().copied()), + ), )?) }) .collect::>()? @@ -702,7 +716,7 @@ fn compile_func( .ret_types .iter() .zip(&values) - .filter_map(|(type_id, value)| { + .filter_map(|(type_id, (_, value))| { let type_info = registry.get_type(type_id).unwrap(); if type_info.is_zst(registry) || type_info.is_memory_allocated(registry) @@ -718,7 +732,7 @@ fn compile_func( .ret_types .iter() .zip(&values) - .filter_map(|(type_id, value)| { + .filter_map(|(type_id, (_, value))| { let type_info = registry.get_type(type_id).unwrap(); if type_info.is_zst(registry) { None @@ -756,7 +770,7 @@ fn compile_func( let (_ret_type_id, ret_type_info) = return_type_infos[0]; let ret_layout = ret_type_info.layout(registry)?; - let ptr = values.remove(0); + let (_, ptr) = values.remove(0); block.append_operation(llvm::store( context, ptr, @@ -774,7 +788,7 @@ fn compile_func( let res_ty = llvm::r#type::r#struct(context, &return_types, false); values.iter().enumerate().try_fold( block.append_op_result(llvm::undef(res_ty, location))?, - |acc, (idx, x)| { + |acc, (idx, (_, x))| { block.append_op_result(llvm::insert_value( context, acc, @@ -1193,7 +1207,7 @@ fn generate_branching_targets<'ctx, 'this, 'a>( statements: &'this [Statement], statement_idx: StatementIdx, invocation: &'this Invocation, - state: &OrderedHashMap>, + state: &OrderedHashMap)>, ) -> Vec<(&'this Block<'ctx>, Vec>)> where 'this: 'ctx, @@ -1212,7 +1226,7 @@ where .map(|var_id| { match branch.results.iter().find_position(|id| *id == var_id) { Some((i, _)) => BranchArg::Returned(i), - None => BranchArg::External(state[var_id]), + None => BranchArg::External(state[var_id].1), } }) .collect::>(); @@ -1233,7 +1247,7 @@ where .find_map(|(i, id)| (id == var_id).then_some(i)) { Some(i) => BranchArg::Returned(i), - None => BranchArg::External(state[var_id]), + None => BranchArg::External(state[var_id].1), } }) .collect::>(); @@ -1360,4 +1374,3 @@ mod trace_dump { .unwrap(); } } - diff --git a/src/metadata/trace_dump.rs b/src/metadata/trace_dump.rs index d4912dd08..0a8cbb1bf 100644 --- a/src/metadata/trace_dump.rs +++ b/src/metadata/trace_dump.rs @@ -12,7 +12,7 @@ use melior::{ r#type::{FunctionType, IntegerType, MemRefType}, Block, Identifier, Location, Module, Region, Value, }, - Context, + Context, ExecutionEngine, }; use std::collections::HashSet; @@ -159,4 +159,24 @@ impl TraceDumpMeta { Ok(()) } + + pub fn register_impls(&self, engine: &ExecutionEngine) { + if self.bindings.contains(&TraceBinding::State) { + unsafe { + engine.register_symbol( + "cairo_native__trace_dump__push", + cairo_native_runtime::trace_dump::cairo_native__trace_dump__push as *mut (), + ); + } + } + + if !self.bindings.is_empty() { + unsafe { + engine.register_symbol( + "cairo_native__trace_dump__state", + cairo_native_runtime::trace_dump::cairo_native__trace_dump__state as *mut (), + ); + } + } + } } diff --git a/src/utils.rs b/src/utils.rs index 5734716eb..0e18d5580 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -203,6 +203,12 @@ pub fn create_engine( .unwrap() .register_impls(&engine); + #[cfg(feature = "with-trace-dump")] + _metadata + .get::() + .unwrap() + .register_impls(&engine); + engine } @@ -323,12 +329,10 @@ pub fn register_runtime_symbols(engine: &ExecutionEngine) { ); #[cfg(feature = "with-cheatcode")] - { - engine.register_symbol( - "cairo_native__vtable_cheatcode", - crate::starknet::cairo_native__vtable_cheatcode as *mut (), - ); - } + engine.register_symbol( + "cairo_native__vtable_cheatcode", + crate::starknet::cairo_native__vtable_cheatcode as *mut (), + ); } } From dd2258ede9e7f88f14fe66f07bf864078e3c7199 Mon Sep 17 00:00:00 2001 From: Julian Gonzalez Calderon Date: Tue, 10 Sep 2024 14:25:43 -0300 Subject: [PATCH 04/17] Add trace dump contracts 2 (#783) * Impl ClassHash and SegmentArena * Add trace dump support to compiler * Reset workflows * Update lock * Implement BoundedInt * Fix Enum trace dump * Make gas_metadata public * Update deps * Fix bug * Move deps to optional (trace dump featire) --- runtime/Cargo.toml | 4 +++ runtime/src/lib.rs | 82 ++++++++++++++++++++++++--------------------- src/executor/aot.rs | 2 +- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index c49e62254..9d33932d8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -13,6 +13,8 @@ with-trace-dump = [ "dep:cairo-lang-sierra", "dep:cairo-lang-utils", "dep:sierra-emu", + "dep:num-bigint", + "dep:num-traits", ] [dependencies] @@ -27,6 +29,8 @@ starknet-curve = "0.5.0" lazy_static = "1.5.0" rand = "0.8.5" +num-bigint = { version = "0.4.4", optional = true } +num-traits = { version = "0.2", optional = true } cairo-lang-sierra = { version = "2.8.0", optional = true } cairo-lang-utils = { version = "2.8.0", optional = true } sierra-emu = { git = "https://github.com/lambdaclass/sierra-emu", optional = true } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a6d4d9270..3ea1d00db 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -476,6 +476,7 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_try_finalize_nz( pub mod trace_dump { use cairo_lang_sierra::{ extensions::{ + bounded_int::BoundedIntConcreteType, core::{CoreLibfunc, CoreType, CoreTypeConcrete}, starknet::StarkNetTypeConcrete, }, @@ -484,12 +485,15 @@ pub mod trace_dump { program_registry::ProgramRegistry, }; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; + use num_bigint::BigInt; + use num_traits::One; use sierra_emu::{ProgramTrace, StateDump, Value}; use starknet_crypto::Felt; use std::{ alloc::Layout, collections::HashMap, mem::swap, + ops::Range, ptr::NonNull, sync::{LazyLock, Mutex}, }; @@ -569,6 +573,7 @@ pub mod trace_dump { match type_info { CoreTypeConcrete::Felt252(_) | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::ContractAddress(_)) + | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::ClassHash(_)) | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::StorageAddress(_)) | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::StorageBaseAddress(_)) => { Value::Felt(Felt::from_bytes_le(value_ptr.cast().as_ref())) @@ -581,6 +586,23 @@ pub mod trace_dump { Value::U128(value_ptr.cast().read()) } + CoreTypeConcrete::BoundedInt(BoundedIntConcreteType { range, .. }) => { + let n_bits = ((range.size() - BigInt::one()).bits() as u32).max(1); + let n_bytes = n_bits.next_multiple_of(8) >> 3; + + let data = NonNull::slice_from_raw_parts(value_ptr.cast::(), n_bytes as usize); + + let value = BigInt::from_bytes_le(num_bigint::Sign::Plus, data.as_ref()); + + Value::BoundedInt { + range: Range { + start: range.lower.clone(), + end: range.upper.clone(), + }, + value: value + &range.lower, + } + } + CoreTypeConcrete::EcPoint(_) => { let layout = Layout::new::<()>(); let (x, layout) = { @@ -696,42 +718,28 @@ pub mod trace_dump { Value::Struct(members) } CoreTypeConcrete::Enum(info) => { - let num_variants = match info.variants.len() { - 0 => unreachable!(), - 1 => 0, - n => (n.next_power_of_two().next_multiple_of(8) >> 3) as _, - }; - - let layout = Layout::new::<()>(); - let (tag_value, layout) = match num_variants { - x if x <= 8 => { - let (layout, offset) = layout.extend(Layout::new::()).unwrap(); - ( - value_ptr.byte_add(offset).cast::().read() as usize, - layout, - ) - } - x if x <= 16 => { - let (layout, offset) = layout.extend(Layout::new::()).unwrap(); - ( - value_ptr.byte_add(offset).cast::().read() as usize, - layout, - ) - } - x if x <= 32 => { - let (layout, offset) = layout.extend(Layout::new::()).unwrap(); - ( - value_ptr.byte_add(offset).cast::().read() as usize, - layout, - ) - } - x if x <= 64 => { - let (layout, offset) = layout.extend(Layout::new::()).unwrap(); - ( - value_ptr.byte_add(offset).cast::().read() as usize, - layout, - ) + let tag_bits = info.variants.len().next_power_of_two().trailing_zeros(); + let (tag_value, layout) = match tag_bits { + 0 => todo!(), + width if width <= 8 => { + (value_ptr.cast::().read() as usize, Layout::new::()) } + width if width <= 16 => ( + value_ptr.cast::().read() as usize, + Layout::new::(), + ), + width if width <= 32 => ( + value_ptr.cast::().read() as usize, + Layout::new::(), + ), + width if width <= 64 => ( + value_ptr.cast::().read() as usize, + Layout::new::(), + ), + width if width <= 128 => ( + value_ptr.cast::().read() as usize, + Layout::new::(), + ), _ => todo!(), }; @@ -769,6 +777,7 @@ pub mod trace_dump { | CoreTypeConcrete::Pedersen(_) | CoreTypeConcrete::Poseidon(_) | CoreTypeConcrete::RangeCheck(_) + | CoreTypeConcrete::SegmentArena(_) | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::System(_)) | CoreTypeConcrete::Uint128MulGuarantee(_) => Value::Unit, @@ -790,7 +799,6 @@ pub mod trace_dump { } CoreTypeConcrete::Span(_) => todo!("CoreTypeConcrete::Span"), CoreTypeConcrete::StarkNet(selector) => match selector { - StarkNetTypeConcrete::ClassHash(_) => todo!("StarkNetTypeConcrete::ClassHash"), StarkNetTypeConcrete::Secp256Point(_) => { todo!("StarkNetTypeConcrete::Secp256Point") } @@ -799,9 +807,7 @@ pub mod trace_dump { } _ => unreachable!(), }, - CoreTypeConcrete::SegmentArena(_) => todo!("CoreTypeConcrete::SegmentArena"), CoreTypeConcrete::Bytes31(_) => todo!("CoreTypeConcrete::Bytes31"), - CoreTypeConcrete::BoundedInt(_) => todo!("CoreTypeConcrete::BoundedInt"), } } } diff --git a/src/executor/aot.rs b/src/executor/aot.rs index f14c40891..4c98a62ea 100644 --- a/src/executor/aot.rs +++ b/src/executor/aot.rs @@ -28,7 +28,7 @@ pub struct AotNativeExecutor { #[educe(Debug(ignore))] registry: ProgramRegistry, - gas_metadata: GasMetadata, + pub gas_metadata: GasMetadata, } unsafe impl Send for AotNativeExecutor {} From 52ef1d830aff7e087d6629abed0311b8d0cbb2ef Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Wed, 11 Sep 2024 01:12:55 +0200 Subject: [PATCH 05/17] Fix second bug in tail recursion transformations. --- src/compiler.rs | 397 ++++++++++++++++++++++++++---------------------- 1 file changed, 216 insertions(+), 181 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 10188a67b..62f992eb2 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -99,6 +99,7 @@ use std::{ collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, ops::Deref, }; +use tracing::debug; /// The [BlockStorage] type is used to map each statement into its own entry block (on the right), /// and its landing block (on the left) if required. @@ -604,30 +605,32 @@ fn compile_func( } } - invocation - .branches - .iter() - .zip(libfunc.branch_signatures()) - .zip(helper.results()) - .map(|((branch_info, signature), result_values)| { - assert_eq!( - branch_info.results.len(), - result_values.len(), - "Mismatched number of returned values from branch." - ); - - Ok(edit_state::put_results( - state.clone(), - branch_info.results.iter().zip( - signature - .vars - .iter() - .map(|x| &x.ty) - .zip(result_values.iter().copied()), - ), - )?) - }) - .collect::>()? + StatementCompileResult::Processed( + invocation + .branches + .iter() + .zip(libfunc.branch_signatures()) + .zip(helper.results()) + .map(|((branch_info, signature), result_values)| { + assert_eq!( + branch_info.results.len(), + result_values.len(), + "Mismatched number of returned values from branch." + ); + + Ok(edit_state::put_results( + state.clone(), + branch_info.results.iter().zip( + signature + .vars + .iter() + .map(|x| &x.ty) + .zip(result_values.iter().copied()), + ), + )?) + }) + .collect::>()?, + ) } Statement::Return(var_ids) => { tracing::trace!("Implementing the return statement at {statement_idx}"); @@ -644,117 +647,133 @@ fn compile_func( ); #[cfg(feature = "with-trace-dump")] - self::trace_dump::build_state_snapshot( - metadata.get_or_insert_with( - crate::metadata::trace_dump::TraceDumpMeta::default, - ), - context, - registry, - module, - &pre_entry_block, - block, - Location::unknown(context), - statement_idx, - &state, - ); + if !is_recursive || tailrec_state.is_some() { + self::trace_dump::build_state_snapshot( + metadata.get_or_insert_with( + crate::metadata::trace_dump::TraceDumpMeta::default, + ), + context, + registry, + module, + &pre_entry_block, + block, + Location::unknown(context), + statement_idx, + &state, + ); + } let (_, mut values) = edit_state::take_args(state, var_ids.iter())?; let mut block = *block; - if let Some((depth_counter, recursion_target)) = tailrec_state { - let location = Location::name( - context, - &format!("return(stmt_idx={}, tail_recursion)", statement_idx), - Location::new( - context, - "program.sierra", - sierra_stmt_start_offset + statement_idx.0, - 0, - ), - ); + if is_recursive { + match tailrec_state { + None => { + // If this block is reached it means that a return has been detected + // within a tail-recursive function before the recursive call has + // been generated. Since we don't have the return target block at + // this point we need to defer this return statement's generation. + return Ok(StatementCompileResult::Deferred); + } + Some((depth_counter, recursion_target)) => { + let location = Location::name( + context, + &format!("return(stmt_idx={}, tail_recursion)", statement_idx), + Location::new( + context, + "program.sierra", + sierra_stmt_start_offset + statement_idx.0, + 0, + ), + ); - // Perform tail recursion. - let cont_block = region.insert_block_after(block, Block::new(&[])); + // Perform tail recursion. + let cont_block = region.insert_block_after(block, Block::new(&[])); - let depth_counter_value = - block.append_op_result(memref::load(depth_counter, &[], location))?; - let k0 = block.const_int_from_type( - context, - location, - 0, - Type::index(context), - )?; - let is_zero_depth = block.append_op_result(index::cmp( - context, - CmpiPredicate::Eq, - depth_counter_value, - k0, - location, - ))?; + let depth_counter_value = block.append_op_result(memref::load( + depth_counter, + &[], + location, + ))?; + let k0 = block.const_int_from_type( + context, + location, + 0, + Type::index(context), + )?; + let is_zero_depth = block.append_op_result(index::cmp( + context, + CmpiPredicate::Eq, + depth_counter_value, + k0, + location, + ))?; - let k1 = block.const_int_from_type( - context, - location, - 1, - Type::index(context), - )?; - let depth_counter_value = block.append_op_result(index::sub( - depth_counter_value, - k1, - location, - ))?; - block.append_operation(memref::store( - depth_counter_value, - depth_counter, - &[], - location, - )); + let k1 = block.const_int_from_type( + context, + location, + 1, + Type::index(context), + )?; + let depth_counter_value = block.append_op_result(index::sub( + depth_counter_value, + k1, + location, + ))?; + block.append_operation(memref::store( + depth_counter_value, + depth_counter, + &[], + location, + )); - let recursive_values = match has_return_ptr { - Some(true) => function - .signature - .ret_types - .iter() - .zip(&values) - .filter_map(|(type_id, (_, value))| { - let type_info = registry.get_type(type_id).unwrap(); - if type_info.is_zst(registry) - || type_info.is_memory_allocated(registry) - { - None - } else { - Some(*value) - } - }) - .collect::>(), - Some(false) => function - .signature - .ret_types - .iter() - .zip(&values) - .filter_map(|(type_id, (_, value))| { - let type_info = registry.get_type(type_id).unwrap(); - if type_info.is_zst(registry) { - None - } else { - Some(*value) - } - }) - .collect::>(), - None => todo!(), - }; + let recursive_values = match has_return_ptr { + Some(true) => function + .signature + .ret_types + .iter() + .zip(&values) + .filter_map(|(type_id, (_, value))| { + let type_info = registry.get_type(type_id).unwrap(); + if type_info.is_zst(registry) + || type_info.is_memory_allocated(registry) + { + None + } else { + Some(*value) + } + }) + .collect::>(), + Some(false) => function + .signature + .ret_types + .iter() + .zip(&values) + .filter_map(|(type_id, (_, value))| { + let type_info = registry.get_type(type_id).unwrap(); + if type_info.is_zst(registry) { + None + } else { + Some(*value) + } + }) + .collect::>(), + None => todo!(), + }; - block.append_operation(cf::cond_br( - context, - is_zero_depth, - &cont_block, - &recursion_target, - &[], - &recursive_values, - location, - )); + block.append_operation(cf::cond_br( + context, + is_zero_depth, + &cont_block, + &recursion_target, + &[], + &recursive_values, + location, + )); - block = cont_block; + block = cont_block; + } + } } // Remove ZST builtins from the return values. @@ -802,7 +821,7 @@ fn compile_func( location, )); - Vec::new() + StatementCompileResult::Processed(Vec::new()) } }) }, @@ -984,44 +1003,46 @@ fn generate_function_structure<'c, 'a>( } } - invocation - .branches - .iter() - .zip(libfunc.branch_signatures()) - .map(|(branch, branch_signature)| { - let state = edit_state::put_results( - state.clone(), - branch.results.iter().zip( - branch_signature - .vars - .iter() - .map(|var_info| -> Result<_, Error> { - registry.get_type(&var_info.ty)?.build( - context, - module, - registry, - metadata_storage, - &var_info.ty, - ) - }) - .collect::, _>>()?, - ), - )?; - - let (prev_state, pred_count) = - match predecessors.entry(statement_idx.next(&branch.target)) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert((state.clone(), 0)), - }; - assert!( - prev_state.eq_unordered(&state), - "Branch target states do not match." - ); - *pred_count += 1; + StatementCompileResult::Processed( + invocation + .branches + .iter() + .zip(libfunc.branch_signatures()) + .map(|(branch, branch_signature)| { + let state = edit_state::put_results( + state.clone(), + branch.results.iter().zip( + branch_signature + .vars + .iter() + .map(|var_info| -> Result<_, Error> { + registry.get_type(&var_info.ty)?.build( + context, + module, + registry, + metadata_storage, + &var_info.ty, + ) + }) + .collect::, _>>()?, + ), + )?; + + let (prev_state, pred_count) = + match predecessors.entry(statement_idx.next(&branch.target)) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert((state.clone(), 0)), + }; + assert!( + prev_state.eq_unordered(&state), + "Branch target states do not match." + ); + *pred_count += 1; - Ok(state) - }) - .collect::>()? + Ok(state) + }) + .collect::>()?, + ) } Statement::Return(var_ids) => { tracing::trace!( @@ -1045,7 +1066,7 @@ fn generate_function_structure<'c, 'a>( block.add_argument(ty, location); } - Vec::new() + StatementCompileResult::Processed(Vec::new()) } }) }, @@ -1166,7 +1187,7 @@ fn foreach_statement_in_function( statements: &[Statement], entry_point: StatementIdx, initial_state: S, - mut closure: impl FnMut(StatementIdx, S) -> Result, E>, + mut closure: impl FnMut(StatementIdx, S) -> Result>, E>, ) -> Result<(), E> where S: Clone, @@ -1179,24 +1200,32 @@ where continue; } - let branch_states = closure(statement_idx, state)?; + match closure(statement_idx, state.clone())? { + StatementCompileResult::Processed(branch_states) => { + let branches = match &statements[statement_idx.0] { + Statement::Invocation(x) => x.branches.as_slice(), + Statement::Return(_) => &[], + }; + assert_eq!( + branches.len(), + branch_states.len(), + "Returned number of states must match the number of branches." + ); - let branches = match &statements[statement_idx.0] { - Statement::Invocation(x) => x.branches.as_slice(), - Statement::Return(_) => &[], - }; - assert_eq!( - branches.len(), - branch_states.len(), - "Returned number of states must match the number of branches." - ); + queue.extend( + branches + .iter() + .map(|branch| statement_idx.next(&branch.target)) + .zip(branch_states), + ); + } + StatementCompileResult::Deferred => { + debug!("Statement {statement_idx}'s compilation has been deferred."); - queue.extend( - branches - .iter() - .map(|branch| statement_idx.next(&branch.target)) - .zip(branch_states), - ); + visited.remove(&statement_idx); + queue.insert(0, (statement_idx, state)); + } + } } Ok(()) @@ -1328,6 +1357,12 @@ fn generate_entry_point_wrapper<'c>( Ok(()) } +#[derive(Clone, Debug)] +enum StatementCompileResult { + Processed(T), + Deferred, +} + #[cfg(feature = "with-trace-dump")] mod trace_dump { use crate::{block_ext::BlockExt, metadata::trace_dump::TraceDumpMeta, types::TypeBuilder}; From 57b39dc9341853c95ce672580101817b579e4688 Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Thu, 19 Sep 2024 12:00:21 +0200 Subject: [PATCH 06/17] Fix error. --- Cargo.lock | 35 +++++++++++++++++++++++++++++++++-- runtime/src/lib.rs | 2 +- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c0add147..64301488b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1036,7 +1036,7 @@ dependencies = [ "serde_json", "sha2", "sha3", - "starknet-crypto", + "starknet-crypto 0.6.2", "starknet-types-core", "thiserror-no-std", "zip", @@ -3529,12 +3529,32 @@ dependencies = [ "num-traits 0.2.19", "rfc6979", "sha2", - "starknet-crypto-codegen", + "starknet-crypto-codegen 0.3.3", "starknet-curve 0.4.2", "starknet-ff", "zeroize", ] +[[package]] +name = "starknet-crypto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2a821ad8d98c6c3e4d0e5097f3fe6e2ed120ada9d32be87cd1330c7923a2f0" +dependencies = [ + "crypto-bigint", + "hex", + "hmac", + "num-bigint", + "num-integer", + "num-traits 0.2.19", + "rfc6979", + "sha2", + "starknet-crypto-codegen 0.4.0", + "starknet-curve 0.5.0", + "starknet-types-core", + "zeroize", +] + [[package]] name = "starknet-crypto-codegen" version = "0.3.3" @@ -3546,6 +3566,17 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "starknet-crypto-codegen" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e179dedc3fa6da064e56811d3e05d446aa2f7459e4eb0e3e49378a337235437" +dependencies = [ + "starknet-curve 0.5.0", + "starknet-types-core", + "syn 2.0.77", +] + [[package]] name = "starknet-curve" version = "0.4.2" diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 6110ce77a..ac3e82e97 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -492,7 +492,7 @@ pub mod trace_dump { use num_bigint::BigInt; use num_traits::One; use sierra_emu::{ProgramTrace, StateDump, Value}; - use starknet_crypto::Felt; + use starknet_types_core::felt::Felt; use std::{ alloc::Layout, collections::HashMap, From 30d7a98c9a02a33d02c4a8e60bc30efb657f5e8a Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Tue, 8 Oct 2024 18:01:45 +0200 Subject: [PATCH 07/17] Fix compile error. --- env.sh | 4 ++-- src/compiler.rs | 2 +- src/metadata/trace_dump.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/env.sh b/env.sh index d20ca7fc8..4e85405f0 100644 --- a/env.sh +++ b/env.sh @@ -11,7 +11,7 @@ case $(uname) in MLIR_SYS_190_PREFIX="$(brew --prefix llvm@19)" LLVM_SYS_191_PREFIX="$(brew --prefix llvm@19)" TABLEGEN_190_PREFIX="$(brew --prefix llvm@19)" - CAIRO_NATIVE_RUNTIME_LIBRARY="$(pwd)/target/debug/libcairo_native_runtime.a" + CAIRO_NATIVE_RUNTIME_LIBRARY="$(pwd)/target/release/libcairo_native_runtime.a" export LIBRARY_PATH export MLIR_SYS_190_PREFIX @@ -24,7 +24,7 @@ case $(uname) in MLIR_SYS_190_PREFIX=/usr/lib/llvm-19 LLVM_SYS_191_PREFIX=/usr/lib/llvm-19 TABLEGEN_190_PREFIX=/usr/lib/llvm-19 - CAIRO_NATIVE_RUNTIME_LIBRARY="$(pwd)/target/debug/libcairo_native_runtime.a" + CAIRO_NATIVE_RUNTIME_LIBRARY="$(pwd)/target/release/libcairo_native_runtime.a" export MLIR_SYS_190_PREFIX export LLVM_SYS_191_PREFIX diff --git a/src/compiler.rs b/src/compiler.rs index 7f08dbe08..52a337d6e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1430,7 +1430,7 @@ enum StatementCompileResult { #[cfg(feature = "with-trace-dump")] mod trace_dump { - use crate::{block_ext::BlockExt, metadata::trace_dump::TraceDumpMeta, types::TypeBuilder}; + use crate::{metadata::trace_dump::TraceDumpMeta, types::TypeBuilder, utils::BlockExt}; use cairo_lang_sierra::{ extensions::core::{CoreLibfunc, CoreType}, ids::{ConcreteTypeId, VarId}, diff --git a/src/metadata/trace_dump.rs b/src/metadata/trace_dump.rs index 0a8cbb1bf..2d3999ec0 100644 --- a/src/metadata/trace_dump.rs +++ b/src/metadata/trace_dump.rs @@ -1,6 +1,6 @@ #![cfg(feature = "with-trace-dump")] -use crate::{block_ext::BlockExt, error::Result}; +use crate::{error::Result, utils::BlockExt}; use cairo_lang_sierra::{ ids::{ConcreteTypeId, VarId}, program::StatementIdx, From e1ba709f198fea8f04f4ae133d9f4a755b2f7803 Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Tue, 8 Oct 2024 18:09:00 +0200 Subject: [PATCH 08/17] Fix more errors. --- src/bin/cairo-native-run.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index c850c4a29..65eb96ecf 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -108,7 +108,7 @@ fn main() -> anyhow::Result<()> { #[cfg(feature = "with-trace-dump")] { use cairo_lang_sierra::program_registry::ProgramRegistry; - use cairo_native::types::TypeBuilder; + use cairo_native::TypeBuilder; use cairo_native_runtime::trace_dump::{TraceDump, TRACE_DUMP}; TRACE_DUMP.lock().unwrap().insert( diff --git a/src/lib.rs b/src/lib.rs index 0560c413c..8705d6c4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ pub use self::{ }; #[cfg(feature = "with-trace-dump")] -pub use cairo_native_runtime as runtime; +pub use {self::types::TypeBuilder, cairo_native_runtime as runtime}; mod arch; pub mod cache; From c989b31dac91db3b48e7bd5de7e9c511a416f022 Mon Sep 17 00:00:00 2001 From: Julian Gonzalez Calderon Date: Mon, 21 Oct 2024 04:38:46 -0300 Subject: [PATCH 09/17] Add trace dump contracts 2 (#838) * Add logs * Implement Felt252Dict trace dump * Implement nullabel for trace dump * Print type id * Remove print * add convertion for Sints * fix mispelled types * forgot to add feature * change Sints value to correspond to sierra-emu * Readd feature * Add is_infinity field to secp256 point struct (#828) * add is_infinity to secp point * clean * fix * use same deps as blockifier * fix * fix layout * fix test * remove some brittle asserts due to random initial data when adding and muling points at infinity * fix * remove unused dep * fixes, missed the value entry/output * Reset src * Fix warnings * Make library public * Use debug runtime for trace dump * fix storage's values truncations (#839) * fix storage's values truncations * change implementation for i8 i16 i32 i64 too * format * Add ptr check to felt dict entry * Add malloc tracing and fix more memory leaks. (#833) * Add memory tracing. * Partial memory fixes. * Fix arrays. * Simplify dictionaries. Fix false positive memory leak in dictionary drops. * Fix zero-sized allocation. Fix warnings. More mem tracing checks. * Fix mem tracing. * Fix memory leak in `print` libfunc. * Fix array slice libfunc. * Fix starknet memory leaks. * Fix keccak syscall. * Fix dict get double free bug. * Lots of memory fixes. * More fixes. * Fix CI. * Remove old TODOs. * Fix CI (again). * Remove TODOs. * Fix test. * Maybe fix * Maybe fix * Maybe fix * Dont panic when building circuit partial outputs * trace dump for Sha256StateHandler * Builtin costs rework (#837) * Update to 2.8.4, release docs, alpha This PR updates cairo to 2.8.4, adds some release docs and updates the version to alpha.3 to prepare for another release. * try fix * cleanup ci, remove panic in link * rename from jit to from ptr, etc * crates.io badge * progress * progress * fmt2 * progress * progress * fix * fix bench * use struct * Fix felt252 and enum deserialization bugs. (#844) * Fix felt252 and enum deserialization bugs. * Fix formatting. * Also fix the runtime. * Fix errors. * try to fix ci * remove unused deps --------- Co-authored-by: Edgar Luque * Fix trace dump type conv * fix aot contract executor not passing builtinstats (#849) * Fix felt252 and enum deserialization bugs. * Fix formatting. * Also fix the runtime. * Fix errors. * try to fix ci * remove unused deps * fix aot contract executor not passing builtinstats --------- Co-authored-by: Esteve Soler Arderiu Co-authored-by: Esteve Soler Arderiu * Fix bug (#851) * Also fix felt bits in starknet syscall wrappers (#853) * Also fix felt bits in starknet syscall wrappers * style * missed * fix aot contract executor not passing builtinstats (#849) * Fix felt252 and enum deserialization bugs. * Fix formatting. * Also fix the runtime. * Fix errors. * try to fix ci * remove unused deps * fix aot contract executor not passing builtinstats --------- Co-authored-by: Esteve Soler Arderiu Co-authored-by: Esteve Soler Arderiu * Fix bug (#851) * Also fix felt bits in starknet syscall wrappers (#853) * Also fix felt bits in starknet syscall wrappers * style * missed * update version to alpha 4 (#854) * bytes31 * Better function attributes and re-enable >O1 opt (#843) * Fix felt252 and enum deserialization bugs. * Fix formatting. * Also fix the runtime. * Fix errors. * try to fix ci * remove unused deps * proper function attributes * add proper function attrs to optimize better, add some passes, run tests with atleast some opts * dont use remi * oops * maybe with opt level 3 now it works * test * works * readd deleted bench * remove dbg * Update bench-hyperfine.sh * fixci * comment * Update src/ffi.rs Co-authored-by: MrAzteca --------- Co-authored-by: Esteve Soler Arderiu Co-authored-by: Esteve Soler Arderiu Co-authored-by: MrAzteca * Resolve `CAIRO_NATIVE_RUNTIME_LIBRARY` relative path (#841) * feat(ffi): resolve runtime relative path using current dir * chore: remove mentions to old runtime variable * fix: typo --------- Co-authored-by: Bohdan Ohorodnii * Implement secp * Fix bytes31 * try to fix ci (#858) * update implementing libfuncs doc (#856) * Fix bytes31 bug * Remove unused dep --------- Co-authored-by: FrancoGiachetta Co-authored-by: Edgar Co-authored-by: MrAzteca Co-authored-by: Esteve Soler Arderiu Co-authored-by: Esteve Soler Arderiu Co-authored-by: Rodrigo Co-authored-by: Bohdan Ohorodnii --- .github/workflows/bench-hyperfine.yml | 10 +- .github/workflows/ci.yml | 31 +- .github/workflows/publish.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/rustdoc.yml | 2 +- Cargo.lock | 89 +-- Cargo.toml | 18 +- Makefile | 27 +- docs/execution_walkthrough.md | 2 +- docs/implementing_libfuncs.md | 78 +- examples/erc20.rs | 6 +- examples/starknet.rs | 6 +- programs/benches/factorial_2M.c | 5 + programs/benches/fib_2M.c | 5 + programs/benches/logistic_map.c | 5 + runtime/Cargo.toml | 3 +- runtime/src/lib.rs | 360 ++++++---- scripts/bench-hyperfine.sh | 2 +- src/arch.rs | 27 +- src/arch/aarch64.rs | 12 + src/arch/x86_64.rs | 12 + src/bin/cairo-native-run.rs | 2 +- src/bin/utils/mod.rs | 36 +- src/compiler.rs | 58 +- src/error.rs | 3 + src/executor.rs | 98 ++- src/executor/aot.rs | 12 + src/executor/contract.rs | 97 ++- src/executor/jit.rs | 13 + src/ffi.rs | 46 +- src/lib.rs | 6 +- src/libfuncs.rs | 4 +- src/libfuncs/array.rs | 525 +++++++++----- src/libfuncs/box.rs | 38 +- src/libfuncs/debug.rs | 23 +- src/libfuncs/felt252.rs | 9 +- src/libfuncs/felt252_dict.rs | 3 +- src/libfuncs/felt252_dict_entry.rs | 259 ++++--- src/libfuncs/function_call.rs | 10 +- src/libfuncs/gas.rs | 135 +++- src/libfuncs/nullable.rs | 38 +- src/libfuncs/sint128.rs | 47 +- src/libfuncs/sint16.rs | 41 +- src/libfuncs/sint32.rs | 41 +- src/libfuncs/sint64.rs | 41 +- src/libfuncs/sint8.rs | 41 +- src/libfuncs/starknet.rs | 208 ++---- src/metadata/drop_overrides.rs | 17 +- src/metadata/dup_overrides.rs | 17 +- src/metadata/gas.rs | 15 +- src/metadata/realloc_bindings.rs | 76 +- src/metadata/runtime_bindings.rs | 259 +++++-- src/starknet.rs | 398 +++++------ src/starknet_stub.rs | 672 +++++++----------- src/types.rs | 16 +- src/types/builtin_costs.rs | 8 +- src/types/circuit.rs | 4 +- src/types/felt252_dict.rs | 24 +- src/types/felt252_dict_entry.rs | 17 +- src/types/squashed_felt252_dict.rs | 11 +- src/types/starknet.rs | 72 +- src/utils.rs | 71 +- src/utils/mem_tracing.rs | 131 ++++ src/values.rs | 112 +-- tests/common.rs | 16 +- .../starknet/contracts/test_u256_order.cairo | 8 +- tests/tests/starknet/keccak.rs | 2 +- tests/tests/starknet/secp256.rs | 541 +++++++------- tests/tests/starknet/syscalls.rs | 8 +- tests/tests/starknet/u256.rs | 2 +- 70 files changed, 2933 insertions(+), 2102 deletions(-) create mode 100644 src/utils/mem_tracing.rs diff --git a/.github/workflows/bench-hyperfine.yml b/.github/workflows/bench-hyperfine.yml index e31bf980a..6182f709d 100644 --- a/.github/workflows/bench-hyperfine.yml +++ b/.github/workflows/bench-hyperfine.yml @@ -16,7 +16,7 @@ env: jobs: bench-hyperfine: name: Hyperfine - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ @@ -34,7 +34,7 @@ jobs: sudo apt-get remove -y 'php.*' sudo apt-get remove -y '^dotnet-.*' sudo apt-get remove -y '^temurin-.*' - sudo apt-get remove -y azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox powershell mono-devel + sudo apt-get remove -y azure-cli microsoft-edge-stable google-chrome-stable firefox mono-devel sudo apt-get autoremove -y sudo apt-get clean df -h @@ -97,7 +97,7 @@ jobs: matrix: branch: [ base, head ] name: Build cairo-native-run for ${{ matrix.branch }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Cache binary uses: actions/cache@v3 @@ -124,7 +124,7 @@ jobs: sudo apt-get remove -y 'php.*' sudo apt-get remove -y '^dotnet-.*' sudo apt-get remove -y '^temurin-.*' - sudo apt-get remove -y azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox powershell mono-devel + sudo apt-get remove -y azure-cli microsoft-edge-stable google-chrome-stable firefox mono-devel sudo apt-get autoremove -y sudo apt-get clean df -h @@ -171,7 +171,7 @@ jobs: hyperfine-prs: name: Bench PR (linux, amd64) needs: [ build-binaries ] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: PROGRAM: fib_2M OUTPUT_DIR: bench-outputs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fe0da0d0..37d2f612e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: check: name: clippy - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ @@ -38,7 +38,7 @@ jobs: fmt: name: rustfmt - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.81.0 @@ -81,12 +81,11 @@ jobs: # Check for unnecessary dependencies. udeps: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ TABLEGEN_190_PREFIX: /usr/lib/llvm-19/ - RUSTUP_TOOLCHAIN: nightly # udeps needs nightly steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master @@ -102,14 +101,12 @@ jobs: keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key - name: Install LLVM run: sudo apt-get install llvm-19 llvm-19-dev llvm-19-runtime clang-19 clang-tools-19 lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools - - name: "Download and run cargo-udeps" - run: | - wget -O - -c https://github.com/est31/cargo-udeps/releases/download/v0.1.50/cargo-udeps-v0.1.50-x86_64-unknown-linux-gnu.tar.gz | tar -xz - cargo-udeps-*/cargo-udeps udeps --all-targets --all-features + - name: Machete + uses: bnjbvr/cargo-machete@main test: name: test (linux, amd64) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ @@ -127,7 +124,7 @@ jobs: sudo apt-get remove -y 'php.*' sudo apt-get remove -y '^dotnet-.*' sudo apt-get remove -y '^temurin-.*' - sudo apt-get remove -y azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox powershell mono-devel + sudo apt-get remove -y azure-cli microsoft-edge-stable google-chrome-stable firefox mono-devel sudo apt-get autoremove -y sudo apt-get clean df -h @@ -188,7 +185,7 @@ jobs: coverage: name: coverage - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 strategy: matrix: partition: [1, 2, 3, 4] @@ -218,7 +215,7 @@ jobs: sudo apt-get remove -y 'php.*' sudo apt-get remove -y '^dotnet-.*' sudo apt-get remove -y '^temurin-.*' - sudo apt-get remove -y azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox powershell mono-devel + sudo apt-get remove -y azure-cli microsoft-edge-stable google-chrome-stable firefox mono-devel sudo apt-get autoremove -y sudo apt-get clean df -h @@ -253,11 +250,11 @@ jobs: run: make runtime-ci && make check-llvm && make needs-cairo2 && make build-alexandria - name: Run tests and generate coverage partition ${{ matrix.partition }} - run: cargo llvm-cov nextest --verbose --all-features --workspace --lcov --output-path ${{ matrix.output }} --partition count:${{ matrix.partition }}/4 + run: cargo llvm-cov nextest --verbose --features=scarb --workspace --lcov --output-path ${{ matrix.output }} --partition count:${{ matrix.partition }}/4 - name: test and generate coverage corelib if: ${{ matrix.partition == '1' }} - run: cargo llvm-cov nextest --verbose --all-features --lcov --output-path lcov-test.info run --bin cairo-native-test -- corelib + run: cargo llvm-cov nextest --verbose --features=scarb --lcov --output-path lcov-test.info run --bin cairo-native-test -- corelib - name: save coverage data with corelib if: ${{ matrix.partition == '1' }} @@ -277,7 +274,7 @@ jobs: upload-coverage: name: Upload Coverage - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: [coverage] steps: - name: Setup rust env @@ -320,7 +317,7 @@ jobs: dockerfile: name: dockerfile (linux, amd64) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: check and free hdd space left @@ -333,7 +330,7 @@ jobs: sudo apt-get remove -y 'php.*' sudo apt-get remove -y '^dotnet-.*' sudo apt-get remove -y '^temurin-.*' - sudo apt-get remove -y azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox powershell mono-devel + sudo apt-get remove -y azure-cli microsoft-edge-stable google-chrome-stable firefox mono-devel sudo apt-get autoremove -y sudo apt-get clean df -h diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8dc69fb33..994bdd853 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,7 +11,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf8e81d31..d9ec91f76 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index c7a8b2fbb..afcc7c42f 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -11,7 +11,7 @@ permissions: jobs: publish-docs: name: GitHub Pages - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ diff --git a/Cargo.lock b/Cargo.lock index 5249670ef..8e6cc3276 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", @@ -950,14 +950,17 @@ dependencies = [ [[package]] name = "cairo-native" -version = "0.2.0-alpha.3" +version = "0.2.0-alpha.4" dependencies = [ "anyhow", "aquamarine", + "ark-ec", + "ark-ff", + "ark-secp256k1", + "ark-secp256r1", "bumpalo", "cairo-lang-compiler", "cairo-lang-defs", - "cairo-lang-diagnostics", "cairo-lang-filesystem", "cairo-lang-runner", "cairo-lang-semantic", @@ -977,7 +980,6 @@ dependencies = [ "criterion", "educe", "itertools 0.13.0", - "k256", "keccak", "lambdaworks-math", "lazy_static", @@ -987,14 +989,13 @@ dependencies = [ "melior", "mlir-sys", "num-bigint", + "num-integer", "num-traits 0.2.19", - "p256", "pretty_assertions_sorted", "proptest", "rstest", "scarb-metadata", "scarb-ui", - "sec1", "serde", "serde_json", "sha2", @@ -1011,7 +1012,7 @@ dependencies = [ [[package]] name = "cairo-native-runtime" -version = "0.2.0-alpha.3" +version = "0.2.0-alpha.4" dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-gas", @@ -1090,9 +1091,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.28" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "jobserver", "libc", @@ -1164,9 +1165,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -1174,9 +1175,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -1496,18 +1497,18 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", @@ -1517,9 +1518,9 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn 2.0.79", @@ -2216,9 +2217,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -2317,9 +2318,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" @@ -2921,9 +2922,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -3219,9 +3220,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -3367,9 +3368,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "6dbcf9b78a125ee667ae19388837dd12294b858d101fdd393cb9d5501ef09eb2" dependencies = [ "itoa", "memchr", @@ -3436,7 +3437,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sierra-emu" version = "0.1.0" -source = "git+https://github.com/lambdaclass/sierra-emu#40ee99489effa22fd806d0ccabe4ed8c3bb87629" +source = "git+https://github.com/lambdaclass/sierra-emu#071caea75a403fb940a96b574f2a70dc431e0662" dependencies = [ "cairo-lang-compiler", "cairo-lang-filesystem", @@ -3708,7 +3709,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ecbc9175dd38627cd01d546e7b41c9a115e5773f4c98f64e2185c81ec5f45ab" dependencies = [ - "bindgen 0.69.4", + "bindgen 0.69.5", "cc", "paste", "thiserror", @@ -4121,9 +4122,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -4132,9 +4133,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -4147,9 +4148,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4157,9 +4158,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -4170,15 +4171,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index efac454d9..81e7802c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cairo-native" -version = "0.2.0-alpha.3" +version = "0.2.0-alpha.4" edition = "2021" license = "Apache-2.0" description = "A compiler to convert Cairo's intermediate representation Sierra code to MLIR." @@ -47,9 +47,10 @@ build-cli = [ "dep:colored", ] scarb = ["build-cli", "dep:scarb-ui", "dep:scarb-metadata"] +with-cheatcode = [] with-debug-utils = [] +with-mem-tracing = [] with-runtime = ["dep:cairo-native-runtime"] -with-cheatcode = [] with-trace-dump = ["cairo-native-runtime/with-trace-dump"] # the aquamarine dep is only used in docs and cannot be detected as used by cargo udeps @@ -65,7 +66,6 @@ cairo-lang-filesystem = "2.8.4" cairo-lang-semantic = "2.8.4" cairo-lang-sierra = "2.8.4" cairo-lang-sierra-generator = "2.8.4" -cairo-lang-diagnostics = "2.8.4" educe = "0.5.11" # can't update until https://github.com/magiclen/educe/issues/27 itertools = "0.13.0" lazy_static = "1.5" @@ -92,7 +92,7 @@ cairo-lang-sierra-gas = "2.8.4" cairo-lang-starknet = "2.8.4" cairo-lang-utils = "2.8.4" cairo-lang-starknet-classes = "2.8.4" -cairo-native-runtime = { version = "0.2.0-alpha.3", path = "runtime", optional = true } +cairo-native-runtime = { version = "0.2.0-alpha.4", path = "runtime", optional = true } clap = { version = "4.5.19", features = ["derive"], optional = true } libloading = "0.8.5" tracing-subscriber = { version = "0.3.18", features = [ @@ -107,15 +107,19 @@ cairo-lang-runner = { version = "2.8.4", optional = true } colored = { version = "2.1.0", optional = true } # needed to interface with cairo-lang-* keccak = "0.1.5" -k256 = "0.13.4" -p256 = "0.13.2" sha2 = "0.10.8" # needed for the syscall handler stub scarb-metadata = { version = "1.12.0", optional = true } scarb-ui = { version = "0.1.5", optional = true } -sec1 = "0.7.3" serde_json = { version = "1.0.128" } stats_alloc = "0.1.10" +# for the syscallhandler stub to match blockifier +ark-secp256k1 = "0.4.0" +ark-secp256r1 = "0.4.0" +ark-ec = "0.4.2" +ark-ff = "0.4.2" +num-integer = "0.1.46" + [dev-dependencies] cairo-vm = { version = "1.0.1", features = ["cairo-1-hints"] } cairo-lang-runner = "2.8.4" diff --git a/Makefile b/Makefile index c88fc21fd..6be88f858 100644 --- a/Makefile +++ b/Makefile @@ -46,15 +46,15 @@ endif .PHONY: build build: check-llvm runtime - cargo build --release --features build-cli,with-cheatcode,with-runtime + cargo build --release --features=scarb .PHONY: build-natives build-native: check-llvm runtime - RUSTFLAGS="-C target-cpu=native" cargo build --release --features build-cli,with-cheatcode,with-runtime + RUSTFLAGS="-C target-cpu=native" cargo build --release --features=scarb .PHONY: build-dev build-dev: check-llvm - cargo build --profile optimized-dev --features build-cli,with-cheatcode,with-runtime + cargo build --profile optimized-dev --features=scarb .PHONY: check check: check-llvm @@ -63,7 +63,7 @@ check: check-llvm .PHONY: test test: check-llvm needs-cairo2 build-alexandria runtime-ci - cargo test --profile ci --features build-cli,with-cheatcode,with-runtime + cargo test --profile ci --features=scarb,with-cheatcode,with-debug-utils .PHONY: test-cairo test-cairo: check-llvm needs-cairo2 build-alexandria runtime-ci @@ -71,20 +71,20 @@ test-cairo: check-llvm needs-cairo2 build-alexandria runtime-ci .PHONY: proptest proptest: check-llvm needs-cairo2 runtime-ci - cargo test --profile ci --features build-cli,with-cheatcode,with-runtime proptest + cargo test --profile ci --features=scarb,with-cheatcode,with-debug-utils proptest .PHONY: test-cli test-ci: check-llvm needs-cairo2 build-alexandria runtime-ci - cargo test --profile ci --features build-cli,with-cheatcode,with-runtime + cargo test --profile ci --features=scarb,with-cheatcode,with-debug-utils .PHONY: proptest-cli proptest-ci: check-llvm needs-cairo2 runtime-ci - cargo test --profile ci --features build-cli,with-cheatcode,with-runtime proptest + cargo test --profile ci --features=scarb,with-cheatcode,with-debug-utils proptest .PHONY: coverage coverage: check-llvm needs-cairo2 build-alexandria runtime-ci - cargo llvm-cov --verbose --profile ci --features build-cli,with-cheatcode,with-runtime --workspace --lcov --output-path lcov.info - cargo llvm-cov --verbose --profile ci --features build-cli,with-cheatcode,with-runtime --lcov --output-path lcov-test.info run --bin cairo-native-test -- corelib + cargo llvm-cov --verbose --profile ci --features=scarb,with-cheatcode,with-debug-utils --workspace --lcov --output-path lcov.info + cargo llvm-cov --verbose --profile ci --features=scarb,with-cheatcode,with-debug-utils --lcov --output-path lcov-test.info run --bin cairo-native-test -- corelib .PHONY: doc doc: check-llvm @@ -95,12 +95,13 @@ doc-open: check-llvm cargo doc --features build-cli,with-cheatcode,with-runtime --no-deps --workspace --open .PHONY: bench -bench: build needs-cairo2 runtime +bench: needs-cairo2 runtime + cargo b --release --bin cairo-native-run ./scripts/bench-hyperfine.sh .PHONY: bench-ci bench-ci: check-llvm needs-cairo2 runtime - cargo criterion --features build-cli,with-cheatcode,with-runtime + cargo criterion --features=scarb,with-cheatcode,with-debug-utils .PHONY: stress-test stress-test: check-llvm @@ -116,7 +117,7 @@ stress-clean: .PHONY: install install: check-llvm - RUSTFLAGS="-C target-cpu=native" cargo install --features build-cli,with-cheatcode,with-runtime --locked --path . + RUSTFLAGS="-C target-cpu=native" cargo install --features=scarb,with-cheatcode --locked --path . .PHONY: clean clean: stress-clean @@ -180,7 +181,7 @@ runtime: cargo b --release -p cairo-native-runtime && cp target/release/libcairo_native_runtime.a . runtime-with-trace-dump: - cargo b --release -p cairo-native-runtime --features=with-trace-dump && cp target/release/libcairo_native_runtime.a . + cargo b -p cairo-native-runtime --features=with-trace-dump && cp target/debug/libcairo_native_runtime.a . .PHONY: runtime-ci runtime-ci: diff --git a/docs/execution_walkthrough.md b/docs/execution_walkthrough.md index fd6b645bb..d5b78d26f 100644 --- a/docs/execution_walkthrough.md +++ b/docs/execution_walkthrough.md @@ -206,7 +206,7 @@ Builtin stats: BuiltinStats { bitwise: 1, ec_op: 0, range_check: 1, pedersen: 0, ## The Cairo Native runtime Sometimes we need to use stuff that would be too complicated or error-prone to implement in MLIR, but that we have readily available from Rust. That's when we use the runtime library. -When using the JIT it'll be automatically linked (if compiled with support for it, which is enabled by default). If using the AOT, the `CAIRO_NATIVE_RUNTIME_LIBDIR` environment variable will have to be modified to point to the directory that contains `libcairo_native_runtime.a`, which is built and placed in said folder by `make build`. +When using the JIT it'll be automatically linked (if compiled with support for it, which is enabled by default). If using the AOT, the `CAIRO_NATIVE_RUNTIME_LIBRARY` environment variable will have to be modified to point to the `libcairo_native_runtime.a` file, which is built and placed in said folder by `make build`. Although it's implemented in Rust, its functions use the C ABI and have Rust's name mangling disabled. This means that to the extern observer it's technically indistinguishible from a library written in C. By doing this we're making the functions callable from MLIR. diff --git a/docs/implementing_libfuncs.md b/docs/implementing_libfuncs.md index 31b558713..aaef8d4d2 100644 --- a/docs/implementing_libfuncs.md +++ b/docs/implementing_libfuncs.md @@ -19,46 +19,45 @@ A type that doesn't have size would be `Layout::new::<()>()`, or if the type is a pointer like box: `Layout::new::<*mut ()>()` When adding a type, we also need to add the **serialization** and -**deserialization** functionality, so we can use it with the JIT runner. +**deserialization** functionality to convert the value to a memory representation that works with cairo-native. -You can find this functionality under `src/values.rs` and -`src/values/{typename}.rs`. As you can see, the project is quite organized -if you have a feel of its layout. +You can find this functionality under `src/values.rs`. -Serialization is done using `Serde`, and each type provides a `deserialize` -and `serialize` function. The inner workings of such functions can be a bit -complex due to how the JIT runner works. You need to work with pointers and -unsafe rust. +There is a `Value` enum with all the possible values that can be passed as input/output. -In `values.rs` we should also declare whether the type is complex under -`is_complex` in the `ValueBuilder` trait implementation. +This enum has a `impl` block with 2 important methods `to_ptr` and `from_ptr` which convert the Value into and from said +memory representation, held behind a pointer. + +When passing the values as inputs, there is one more required step, that is to pass the bytes of those values in the +target platform ABI compatible way, this is done with the `AbiArgument` trait and the `to_bytes` method. + +This trait, located in `src/arch.rs` is implemented currently for aarch64 and x86_64 (depending on the host platform) for some basic types, such as u64, u128, pointers, etc. Most importantly, it is also implemented for `ValueWithInfoWrapper` which allows to convert the Value using it's `to_ptr` method and correctly passing it as an argument in the given `buffer: &mut Vec`. + +In `types.rs` we should also declare whether the type is complex under +`is_complex`, whether its a builtin in `is_builtin`, a zst in `is_zst` and define it's layout in the `TypeBuilder` trait implementation. > Complex types are always passed by pointer (both as params and return > values) and require a stack allocation. Examples of complex values include > structs and enums, but not felts since LLVM considers them integers. ### Deserializing a type -When **deserializing** (a.k.a converting the inputs so the JIT runner +When **deserializing** (a.k.a converting the inputs so the runner accepts them), you are passed a bump allocator arena from `Bumpalo`, the general idea is to get the layout and size of the type, allocate it under -the arena, get a pointer, and return it. Which will later be passed to the -MLIR JIT runner. It is important the pointers passed are allocated by the +the arena, get a pointer, and return it (Some types require additional data on the heap, such as arrays, such allocations should be done with libc's malloc.). Which will later be passed to the runner. It is important the pointers passed are allocated by the arena and not Rust itself. -Then we need to hookup de `deserialize` method in `values.rs` `deserialize` -method. +This is done in the `to_ptr` method. ### Serializing a type When **serializing** a type, you will get a `ptr: NonNull<()>` (non null pointer), which you will have to cast, dereference and then deserialize. For a simple type to learn how it works, we recommend checking -`src/values/uint8.rs`, for more complex types, check `src/values/felt252.rs`. +`src/values.rs`, in the `from_ptr` method, look the u8 type in the match, for more complex types, check the felt252 type. The hardest types to understand are the enums, dictionaries and arrays, since they are complex types. -Then we need to hookup de `serialize` method in `values.rs` `serialize` method. - ### Implementing the library function Libfuncs are implemented under `src/libfuncs.rs` and `src/libfuncs/{libfunc_name}.rs`. Just like types. @@ -67,21 +66,15 @@ Using the `src/libfuncs/felt252.rs` libfuncs as a aid: ```rust,ignore /// Select and call the correct libfunc builder function from the selector. -pub fn build<'ctx, 'this, TType, TLibfunc>( +pub fn build<'ctx, 'this>( context: &'ctx Context, - registry: &ProgramRegistry, + registry: &ProgramRegistry, entry: &'this Block<'ctx>, location: Location<'ctx>, helper: &LibfuncHelper<'ctx, 'this>, metadata: &mut MetadataStorage, selector: &Felt252Concrete, -) -> Result<()> -where - TType: GenericType, - TLibfunc: GenericLibfunc, - ::Concrete: TypeBuilder, - ::Concrete: LibfuncBuilder, -{ +) -> Result<()> { match selector { Felt252Concrete::BinaryOperation(info) => { build_binary_operation(context, registry, entry, location, helper, metadata, info) @@ -109,11 +102,11 @@ An example libfunc, converting a u8 to a felt252, extensively commented: ```rust,ignore /// Generate MLIR operations for the `u8_to_felt252` libfunc. -pub fn build_to_felt252<'ctx, 'this, TType, TLibfunc>( +pub fn build_to_felt252<'ctx, 'this>( // The Context from MLIR, this is like the heart of the MLIR API, its required to create most stuff like types. context: &'ctx Context, // This is the sierra program registry, it aids us at finding types, functions, etc. - registry: &ProgramRegistry, + registry: &ProgramRegistry, // This is the MLIR entry block for this libfunc. Remember we append operations to blocks. entry: &'this Block<'ctx>, // The already created MLIR location for this libfunc, we need to pass this to all the MLIR operations. @@ -125,28 +118,24 @@ pub fn build_to_felt252<'ctx, 'this, TType, TLibfunc>( // The sierra information for this specific library function. This libfunc only contains signature information, but // others which are generic over a type will contain information about that type, for example array related libfuncs. info: &SignatureOnlyConcreteLibfunc, -) -> Result<()> -where - TType: GenericType, - TLibfunc: GenericLibfunc, - ::Concrete: TypeBuilder, - ::Concrete: LibfuncBuilder, -{ +) -> Result<()> { // We retrieve the felt252 type from the registry and call the "build" method to create the MLIR type. // We could also just call get_type() to hold on to the sierra type, and then `.layout(registry)` to get the type layout, // which is needed in some libfuncs doing more complex stuff. - let felt252_ty = registry - .get_type(&info.branch_signatures()[0].vars[0].ty)? - .build(context, helper, registry, metadata)?; + let felt252_ty = registry.build_type( + context, + helper, + registry, + metadata, + &info.branch_signatures()[0].vars[0].ty, + )?; // Retrieve the first argument passed to this library function, in this case its the u8 value we need to convert. let value: Value = entry.argument(0)?.into(); - // We create a "extui" operation from the "arith" dialect, which basically zero extends the value to have the same bits as the given type. - let op = entry.append_operation(arith::extui(value, felt252_ty, location)); - - // Get the result from the operation, in this case it's the extended value - let result = op.result(0)?.into(); + // We create a "extui" operation from the "arith" dialect, which basically + // zero extends the value to have the same bits as the given type. + let result = entry.append_op_result(arith::extui(value, felt252_ty, location))?; // Using the helper argument, append the branching operation to the next statement, passing result as our output variable. entry.append_operation(helper.br(0, &[result], location)); @@ -156,4 +145,3 @@ where ``` More info on the `extui` operation: - diff --git a/examples/erc20.rs b/examples/erc20.rs index 594906a2f..76917c32e 100644 --- a/examples/erc20.rs +++ b/examples/erc20.rs @@ -268,10 +268,10 @@ impl StarknetSyscallHandler for SyscallHandler { fn sha256_process_block( &mut self, - _prev_state: &[u32; 8], - _current_block: &[u32; 16], + _state: &mut [u32; 8], + _block: &[u32; 16], _remaining_gas: &mut u128, - ) -> SyscallResult<[u32; 8]> { + ) -> SyscallResult<()> { unimplemented!() } } diff --git a/examples/starknet.rs b/examples/starknet.rs index 6ccbb0771..28f987041 100644 --- a/examples/starknet.rs +++ b/examples/starknet.rs @@ -399,10 +399,10 @@ impl StarknetSyscallHandler for SyscallHandler { fn sha256_process_block( &mut self, - _prev_state: &[u32; 8], - _current_block: &[u32; 16], + _state: &mut [u32; 8], + _block: &[u32; 16], _remaining_gas: &mut u128, - ) -> SyscallResult<[u32; 8]> { + ) -> SyscallResult<()> { unimplemented!() } } diff --git a/programs/benches/factorial_2M.c b/programs/benches/factorial_2M.c index 7db0cfcc6..9beefef5f 100644 --- a/programs/benches/factorial_2M.c +++ b/programs/benches/factorial_2M.c @@ -16,6 +16,7 @@ typedef struct factorial_return_values } result; } factorial_return_values_t; +extern uint64_t* builtin_costs; static void run_bench(factorial_return_values_t*, uint64_t) __attribute__((weakref("_mlir_ciface_factorial_2M::factorial_2M::main(f1)"))); @@ -25,6 +26,10 @@ int main() { factorial_return_values_t return_values; + uint64_t BuiltinCosts[7] = {1, 4050, 583, 4085, 491, 230, 604}; + + builtin_costs = &BuiltinCosts[0]; + run_bench(&return_values, 0); assert(return_values.result.discriminant == 0); diff --git a/programs/benches/fib_2M.c b/programs/benches/fib_2M.c index fecb87cb3..72a420a86 100644 --- a/programs/benches/fib_2M.c +++ b/programs/benches/fib_2M.c @@ -16,6 +16,7 @@ typedef struct fib_return_values } result; } fib_return_values_t; +extern uint64_t* builtin_costs; static void run_bench(fib_return_values_t *, uint64_t) __attribute__((weakref("_mlir_ciface_fib_2M::fib_2M::main(f1)"))); @@ -23,6 +24,10 @@ static void run_bench(fib_return_values_t *, uint64_t) int main() { + uint64_t BuiltinCosts[7] = {1, 4050, 583, 4085, 491, 230, 604}; + + builtin_costs = &BuiltinCosts[0]; + fib_return_values_t return_values; run_bench(&return_values, 0); diff --git a/programs/benches/logistic_map.c b/programs/benches/logistic_map.c index 1294dcdbf..483def4c4 100644 --- a/programs/benches/logistic_map.c +++ b/programs/benches/logistic_map.c @@ -16,6 +16,7 @@ typedef struct map_return_values } result; } map_return_values_t; +extern uint64_t* builtin_costs; static void run_bench(map_return_values_t *, uint64_t) __attribute__((weakref("_mlir_ciface_logistic_map::logistic_map::main(f2)"))); @@ -23,6 +24,10 @@ static void run_bench(map_return_values_t *, uint64_t) int main() { + uint64_t BuiltinCosts[7] = {1, 4050, 583, 4085, 491, 230, 604}; + + builtin_costs = &BuiltinCosts[0]; + map_return_values_t return_values; run_bench(&return_values, 0); diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index fbfa5e17c..a3aeb9224 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cairo-native-runtime" -version = "0.2.0-alpha.3" +version = "0.2.0-alpha.4" description = "A compiler to convert Cairo's intermediate representation Sierra code to MLIR." edition = "2021" license = "Apache-2.0" @@ -20,7 +20,6 @@ with-trace-dump = [ cairo-lang-sierra-gas = "2.8.4" itertools = "0.13.0" lazy_static = "1.5.0" -libc = "0.2" rand = "0.8.5" starknet-curve = "0.5.1" starknet-types-core = { version = "0.1.7", default-features = false, features = [ diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d3c5fd282..af74f3dd1 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -13,7 +13,7 @@ use starknet_types_core::{ felt::Felt, hash::StarkHash, }; -use std::{collections::HashMap, fs::File, io::Write, os::fd::FromRawFd, ptr::NonNull, slice}; +use std::{collections::HashMap, ffi::c_void, fs::File, io::Write, os::fd::FromRawFd}; use std::{ops::Mul, vec::IntoIter}; lazy_static! { @@ -44,7 +44,8 @@ pub unsafe extern "C" fn cairo_native__libfunc__debug__print( let mut items = Vec::with_capacity(len as usize); for i in 0..len as usize { - let data = *data.add(i); + let mut data = *data.add(i); + data[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). let value = Felt::from_bytes_le(&data); items.push(value); @@ -76,22 +77,24 @@ pub unsafe extern "C" fn cairo_native__libfunc__debug__print( /// definitely unsafe to use manually. #[no_mangle] pub unsafe extern "C" fn cairo_native__libfunc__pedersen( - dst: *mut u8, - lhs: *const u8, - rhs: *const u8, + dst: &mut [u8; 32], + lhs: &[u8; 32], + rhs: &[u8; 32], ) { // Extract arrays from the pointers. - let dst = slice::from_raw_parts_mut(dst, 32); - let lhs = slice::from_raw_parts(lhs, 32); - let rhs = slice::from_raw_parts(rhs, 32); + let mut lhs = *lhs; + let mut rhs = *rhs; + + lhs[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + rhs[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). // Convert to FieldElement. - let lhs = Felt::from_bytes_le_slice(lhs); - let rhs = Felt::from_bytes_le_slice(rhs); + let lhs = Felt::from_bytes_le(&lhs); + let rhs = Felt::from_bytes_le(&rhs); // Compute pedersen hash and copy the result into `dst`. let res = starknet_types_core::hash::Pedersen::hash(&lhs, &rhs); - dst.copy_from_slice(&res.to_bytes_le()); + *dst = res.to_bytes_le(); } /// Compute `hades_permutation(op0, op1, op2)` and replace the operands with the results. @@ -108,36 +111,37 @@ pub unsafe extern "C" fn cairo_native__libfunc__pedersen( /// definitely unsafe to use manually. #[no_mangle] pub unsafe extern "C" fn cairo_native__libfunc__hades_permutation( - op0: *mut u8, - op1: *mut u8, - op2: *mut u8, + op0: &mut [u8; 32], + op1: &mut [u8; 32], + op2: &mut [u8; 32], ) { - // Extract arrays from the pointers. - let op0 = slice::from_raw_parts_mut(op0, 32); - let op1 = slice::from_raw_parts_mut(op1, 32); - let op2 = slice::from_raw_parts_mut(op2, 32); + op0[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + op1[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + op2[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). // Convert to FieldElement. let mut state = [ - Felt::from_bytes_le_slice(op0), - Felt::from_bytes_le_slice(op1), - Felt::from_bytes_le_slice(op2), + Felt::from_bytes_le(op0), + Felt::from_bytes_le(op1), + Felt::from_bytes_le(op2), ]; // Compute Poseidon permutation. starknet_types_core::hash::Poseidon::hades_permutation(&mut state); // Write back the results. - op0.copy_from_slice(&state[0].to_bytes_le()); - op1.copy_from_slice(&state[1].to_bytes_le()); - op2.copy_from_slice(&state[2].to_bytes_le()); + *op0 = state[0].to_bytes_le(); + *op1 = state[1].to_bytes_le(); + *op2 = state[2].to_bytes_le(); } /// Felt252 type used in cairo native runtime -#[derive(Debug, Default)] +#[derive(Debug)] pub struct FeltDict { - pub inner: HashMap<[u8; 32], NonNull>, + pub inner: HashMap<[u8; 32], *mut c_void>, pub count: u64, + + pub free_fn: unsafe extern "C" fn(*mut c_void), } /// Allocate a new dictionary. @@ -147,8 +151,14 @@ pub struct FeltDict { /// This function is intended to be called from MLIR, deals with pointers, and is therefore /// definitely unsafe to use manually. #[no_mangle] -pub unsafe extern "C" fn cairo_native__dict_new() -> *mut FeltDict { - Box::into_raw(Box::::default()) +pub unsafe extern "C" fn cairo_native__dict_new( + free_fn: extern "C" fn(*mut c_void), +) -> *mut FeltDict { + Box::into_raw(Box::new(FeltDict { + inner: HashMap::default(), + count: 0, + free_fn, + })) } /// Free a dictionary using an optional callback to drop each element. @@ -157,23 +167,25 @@ pub unsafe extern "C" fn cairo_native__dict_new() -> *mut FeltDict { /// /// This function is intended to be called from MLIR, deals with pointers, and is therefore /// definitely unsafe to use manually. -// Note: Using `Option` is ffi-safe thanks to Option's null +// Note: Using `Option` is ffi-safe thanks to Option's null // pointer optimization. Check out // https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization for more info. #[no_mangle] pub unsafe extern "C" fn cairo_native__dict_drop( ptr: *mut FeltDict, - drop_fn: Option, + drop_fn: Option, ) { - let map = Box::from_raw(ptr); + let dict = Box::from_raw(ptr); // Free the entries manually. - for entry in map.inner.into_values() { - if let Some(drop_fn) = drop_fn { - drop_fn(entry.as_ptr()); - } + for entry in dict.inner.into_values() { + if !entry.is_null() { + if let Some(drop_fn) = drop_fn { + drop_fn(entry); + } - libc::free(entry.as_ptr()); + (dict.free_fn)(entry); + } } } @@ -186,62 +198,45 @@ pub unsafe extern "C" fn cairo_native__dict_drop( #[no_mangle] pub unsafe extern "C" fn cairo_native__dict_dup( ptr: *mut FeltDict, - dup_fn: extern "C" fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void, + dup_fn: extern "C" fn(*mut c_void) -> *mut c_void, ) -> *mut FeltDict { let old_dict = &*ptr; - let mut new_dict = Box::::default(); + let mut new_dict = Box::new(FeltDict { + inner: HashMap::default(), + count: 0, + free_fn: old_dict.free_fn, + }); new_dict.inner.extend( old_dict .inner .iter() - .map(|(&k, &v)| (k, NonNull::new(dup_fn(v.as_ptr())).unwrap())), + .filter_map(|(&k, &v)| (!v.is_null()).then_some((k, dup_fn(v)))), ); Box::into_raw(new_dict) } -/// Return the value (reference) for a given key, or null if not present. Increment the access -/// count. +/// Return a pointer to the entry's value pointer for a given key, inserting a null pointer if not +/// present. Increment the access count. /// -/// # Safety -/// -/// This function is intended to be called from MLIR, deals with pointers, and is therefore -/// definitely unsafe to use manually. -#[no_mangle] -pub unsafe extern "C" fn cairo_native__dict_get( - ptr: *mut FeltDict, - key: &[u8; 32], -) -> *mut std::ffi::c_void { - let dict: &mut FeltDict = &mut *ptr; - dict.count += 1; - - match dict.inner.get(key) { - Some(v) => v.as_ptr(), - None => std::ptr::null_mut(), - } -} - -/// Inserts the provided key value. Returning the old one or nullptr if there was none. +/// The null pointer will be either updated by `felt252_dict_entry_finalize` or removed (along with +/// everything else in the dict) by the entry's drop implementation. /// /// # Safety /// /// This function is intended to be called from MLIR, deals with pointers, and is therefore /// definitely unsafe to use manually. #[no_mangle] -pub unsafe extern "C" fn cairo_native__dict_insert( - ptr: *mut FeltDict, +pub unsafe extern "C" fn cairo_native__dict_get( + dict: &mut FeltDict, key: &[u8; 32], - value: NonNull, -) -> *mut std::ffi::c_void { - let dict = &mut *ptr; - let old_ptr = dict.inner.insert(*key, value); +) -> *mut c_void { + let mut key = *key; + key[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). - if let Some(v) = old_ptr { - v.as_ptr() - } else { - std::ptr::null_mut() - } + dict.count += 1; + dict.inner.entry(key).or_insert(std::ptr::null_mut()) as *mut _ as *mut c_void } /// Compute the total gas refund for the dictionary at squash time. @@ -268,9 +263,10 @@ pub unsafe extern "C" fn cairo_native__dict_gas_refund(ptr: *const FeltDict) -> /// definitely unsafe to use manually. #[no_mangle] pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_point_from_x_nz( - mut point_ptr: NonNull<[[u8; 32]; 2]>, + point_ptr: &mut [[u8; 32]; 2], ) -> bool { - let x = Felt::from_bytes_le(&point_ptr.as_ref()[0]); + point_ptr[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + let x = Felt::from_bytes_le(&point_ptr[0]); // https://github.com/starkware-libs/cairo/blob/aaad921bba52e729dc24ece07fab2edf09ccfa15/crates/cairo-lang-sierra-to-casm/src/invocations/ec.rs#L63 @@ -286,7 +282,7 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_point_from_x_nz( match AffinePoint::new(x, y) { Ok(point) => { - point_ptr.as_mut()[1].copy_from_slice(&point.y().to_bytes_le()); + point_ptr[1] = point.y().to_bytes_le(); true } Err(_) => false, @@ -305,15 +301,18 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_point_from_x_nz( /// definitely unsafe to use manually. #[no_mangle] pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_point_try_new_nz( - mut point_ptr: NonNull<[[u8; 32]; 2]>, + point_ptr: &mut [[u8; 32]; 2], ) -> bool { - let x = Felt::from_bytes_le(&point_ptr.as_ref()[0]); - let y = Felt::from_bytes_le(&point_ptr.as_ref()[1]); + point_ptr[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + point_ptr[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + + let x = Felt::from_bytes_le(&point_ptr[0]); + let y = Felt::from_bytes_le(&point_ptr[1]); match AffinePoint::new(x, y) { Ok(point) => { - point_ptr.as_mut()[0].copy_from_slice(&point.x().to_bytes_le()); - point_ptr.as_mut()[1].copy_from_slice(&point.y().to_bytes_le()); + point_ptr[0] = point.x().to_bytes_le(); + point_ptr[1] = point.y().to_bytes_le(); true } Err(_) => false, @@ -327,9 +326,7 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_point_try_new_nz( /// This function is intended to be called from MLIR, deals with pointers, and is therefore /// definitely unsafe to use manually. #[no_mangle] -pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_init( - mut state_ptr: NonNull<[[u8; 32]; 4]>, -) { +pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_init(state_ptr: &mut [[u8; 32]; 4]) { // https://github.com/starkware-libs/cairo/blob/aaad921bba52e729dc24ece07fab2edf09ccfa15/crates/cairo-lang-runner/src/casm_run/mod.rs#L1802 let mut rng = rand::thread_rng(); let (random_x, random_y) = loop { @@ -345,10 +342,10 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_init( // We already made sure its a valid point. let state = AffinePoint::new_unchecked(random_x, random_y); - state_ptr.as_mut()[0].copy_from_slice(&state.x().to_bytes_le()); - state_ptr.as_mut()[1].copy_from_slice(&state.y().to_bytes_le()); - state_ptr.as_mut()[2].copy_from_slice(&state.x().to_bytes_le()); - state_ptr.as_mut()[3].copy_from_slice(&state.y().to_bytes_le()); + state_ptr[0] = state.x().to_bytes_le(); + state_ptr[1] = state.y().to_bytes_le(); + state_ptr[2] = state_ptr[0]; + state_ptr[3] = state_ptr[1]; } /// Compute `ec_state_add(state, point)` and store the state back. @@ -363,24 +360,31 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_init( /// definitely unsafe to use manually. #[no_mangle] pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_add( - mut state_ptr: NonNull<[[u8; 32]; 4]>, - point_ptr: NonNull<[[u8; 32]; 2]>, + state_ptr: &mut [[u8; 32]; 4], + point_ptr: &[[u8; 32]; 2], ) { + state_ptr[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + state_ptr[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + + let mut point_ptr = *point_ptr; + point_ptr[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + point_ptr[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + // We use unchecked methods because the inputs must already be valid points. let mut state = ProjectivePoint::from_affine_unchecked( - Felt::from_bytes_le(&state_ptr.as_ref()[0]), - Felt::from_bytes_le(&state_ptr.as_ref()[1]), + Felt::from_bytes_le(&state_ptr[0]), + Felt::from_bytes_le(&state_ptr[1]), ); let point = AffinePoint::new_unchecked( - Felt::from_bytes_le(&point_ptr.as_ref()[0]), - Felt::from_bytes_le(&point_ptr.as_ref()[1]), + Felt::from_bytes_le(&point_ptr[0]), + Felt::from_bytes_le(&point_ptr[1]), ); state += &point; let state = state.to_affine().unwrap(); - state_ptr.as_mut()[0].copy_from_slice(&state.x().to_bytes_le()); - state_ptr.as_mut()[1].copy_from_slice(&state.y().to_bytes_le()); + state_ptr[0] = state.x().to_bytes_le(); + state_ptr[1] = state.y().to_bytes_le(); } /// Compute `ec_state_add_mul(state, scalar, point)` and store the state back. @@ -395,26 +399,36 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_add( /// definitely unsafe to use manually. #[no_mangle] pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_add_mul( - mut state_ptr: NonNull<[[u8; 32]; 4]>, - scalar_ptr: NonNull<[u8; 32]>, - point_ptr: NonNull<[[u8; 32]; 2]>, + state_ptr: &mut [[u8; 32]; 4], + scalar_ptr: &[u8; 32], + point_ptr: &[[u8; 32]; 2], ) { + state_ptr[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + state_ptr[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + + let mut point_ptr = *point_ptr; + point_ptr[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + point_ptr[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + + let mut scalar_ptr = *scalar_ptr; + scalar_ptr[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + // Here the points should already be checked as valid, so we can use unchecked. let mut state = ProjectivePoint::from_affine_unchecked( - Felt::from_bytes_le(&state_ptr.as_ref()[0]), - Felt::from_bytes_le(&state_ptr.as_ref()[1]), + Felt::from_bytes_le(&state_ptr[0]), + Felt::from_bytes_le(&state_ptr[1]), ); let point = ProjectivePoint::from_affine_unchecked( - Felt::from_bytes_le(&point_ptr.as_ref()[0]), - Felt::from_bytes_le(&point_ptr.as_ref()[1]), + Felt::from_bytes_le(&point_ptr[0]), + Felt::from_bytes_le(&point_ptr[1]), ); - let scalar = Felt::from_bytes_le(scalar_ptr.as_ref()); + let scalar = Felt::from_bytes_le(&scalar_ptr); state += &point.mul(scalar); let state = state.to_affine().unwrap(); - state_ptr.as_mut()[0].copy_from_slice(&state.x().to_bytes_le()); - state_ptr.as_mut()[1].copy_from_slice(&state.y().to_bytes_le()); + state_ptr[0] = state.x().to_bytes_le(); + state_ptr[1] = state.y().to_bytes_le(); } /// Compute `ec_state_try_finalize_nz(state)` and store the result. @@ -429,17 +443,23 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_add_mul( /// definitely unsafe to use manually. #[no_mangle] pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_try_finalize_nz( - mut point_ptr: NonNull<[[u8; 32]; 2]>, - state_ptr: NonNull<[[u8; 32]; 4]>, + point_ptr: &mut [[u8; 32]; 2], + state_ptr: &[[u8; 32]; 4], ) -> bool { + let mut state_ptr = *state_ptr; + state_ptr[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + state_ptr[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + state_ptr[2][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + state_ptr[3][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + // We use unchecked methods because the inputs must already be valid points. let state = ProjectivePoint::from_affine_unchecked( - Felt::from_bytes_le(&state_ptr.as_ref()[0]), - Felt::from_bytes_le(&state_ptr.as_ref()[1]), + Felt::from_bytes_le(&state_ptr[0]), + Felt::from_bytes_le(&state_ptr[1]), ); let random = ProjectivePoint::from_affine_unchecked( - Felt::from_bytes_le(&state_ptr.as_ref()[2]), - Felt::from_bytes_le(&state_ptr.as_ref()[3]), + Felt::from_bytes_le(&state_ptr[2]), + Felt::from_bytes_le(&state_ptr[3]), ); if state.x() == random.x() && state.y() == random.y() { @@ -448,8 +468,8 @@ pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_try_finalize_nz( let point = &state - &random; let point = point.to_affine().unwrap(); - point_ptr.as_mut()[0].copy_from_slice(&point.x().to_bytes_le()); - point_ptr.as_mut()[1].copy_from_slice(&point.y().to_bytes_le()); + point_ptr[0] = point.x().to_bytes_le(); + point_ptr[1] = point.y().to_bytes_le(); true } @@ -635,16 +655,23 @@ pub mod trace_dump { extensions::{ bounded_int::BoundedIntConcreteType, core::{CoreLibfunc, CoreType, CoreTypeConcrete}, - starknet::StarkNetTypeConcrete, + starknet::{secp256::Secp256PointTypeConcrete, StarkNetTypeConcrete}, }, ids::{ConcreteTypeId, VarId}, program::StatementIdx, program_registry::ProgramRegistry, }; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; + use itertools::Itertools; use num_bigint::BigInt; use num_traits::One; - use sierra_emu::{ProgramTrace, StateDump, Value}; + use sierra_emu::{ + starknet::{ + Secp256k1Point as EmuSecp256k1Point, Secp256r1Point as EmuSecp256r1Point, + U256 as EmuU256, + }, + ProgramTrace, StateDump, Value, + }; use starknet_types_core::felt::Felt; use std::{ alloc::Layout, @@ -655,6 +682,8 @@ pub mod trace_dump { sync::{LazyLock, Mutex}, }; + use crate::FeltDict; + pub static TRACE_DUMP: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -942,29 +971,112 @@ pub mod trace_dump { CoreTypeConcrete::Coupon(_) => todo!("CoreTypeConcrete::Coupon"), CoreTypeConcrete::Circuit(_) => todo!("CoreTypeConcrete::Circuit"), CoreTypeConcrete::Const(_) => todo!("CoreTypeConcrete::Const"), - CoreTypeConcrete::Sint8(_) => todo!("CoreTypeConcrete::Sint8"), + CoreTypeConcrete::Sint8(_) => Value::I8(value_ptr.cast().read()), CoreTypeConcrete::Sint16(_) => todo!("CoreTypeConcrete::Sint16"), - CoreTypeConcrete::Sint32(_) => todo!("CoreTypeConcrete::Sint32"), + CoreTypeConcrete::Sint32(_) => Value::I32(value_ptr.cast().read()), CoreTypeConcrete::Sint64(_) => todo!("CoreTypeConcrete::Sint64"), - CoreTypeConcrete::Sint128(_) => todo!("CoreTypeConcrete::Sint128"), - CoreTypeConcrete::Nullable(_) => todo!("CoreTypeConcrete::Nullable"), + CoreTypeConcrete::Sint128(_) => Value::I128(value_ptr.cast().read()), + CoreTypeConcrete::Nullable(info) => { + let inner_ptr = value_ptr.cast::<*mut ()>().read(); + match NonNull::new(inner_ptr) { + Some(inner_ptr) => read_value_ptr(registry, &info.ty, inner_ptr, get_layout), + None => Value::Uninitialized { + ty: info.ty.clone(), + }, + } + } + CoreTypeConcrete::RangeCheck96(_) => todo!("CoreTypeConcrete::RangeCheck96"), - CoreTypeConcrete::Felt252Dict(_) => todo!("CoreTypeConcrete::Felt252Dict"), + CoreTypeConcrete::Felt252Dict(info) => { + let value = value_ptr.cast::().as_ref(); + + let data = value + .inner + .iter() + .map(|(k, &p)| { + let v = match NonNull::new(p) { + Some(value_ptr) => { + read_value_ptr(registry, &info.ty, value_ptr.cast(), get_layout) + } + None => Value::Uninitialized { + ty: info.ty.clone(), + }, + }; + let k = Felt::from_bytes_le(k); + (k, v) + }) + .collect::>(); + + Value::FeltDict { + ty: info.ty.clone(), + data, + } + } CoreTypeConcrete::Felt252DictEntry(_) => todo!("CoreTypeConcrete::Felt252DictEntry"), CoreTypeConcrete::SquashedFelt252Dict(_) => { todo!("CoreTypeConcrete::SquashedFelt252Dict") } CoreTypeConcrete::Span(_) => todo!("CoreTypeConcrete::Span"), CoreTypeConcrete::StarkNet(selector) => match selector { - StarkNetTypeConcrete::Secp256Point(_) => { - todo!("StarkNetTypeConcrete::Secp256Point") - } + StarkNetTypeConcrete::Secp256Point(selector) => match selector { + Secp256PointTypeConcrete::K1(_) => { + let point: Secp256Point = value_ptr.cast().read(); + let emu_point = EmuSecp256k1Point { + x: EmuU256 { + lo: point.x.lo, + hi: point.x.hi, + }, + y: EmuU256 { + lo: point.y.lo, + hi: point.y.hi, + }, + }; + emu_point.into_value() + } + Secp256PointTypeConcrete::R1(_) => { + let point: Secp256Point = value_ptr.cast().read(); + let emu_point = EmuSecp256r1Point { + x: EmuU256 { + lo: point.x.lo, + hi: point.x.hi, + }, + y: EmuU256 { + lo: point.y.lo, + hi: point.y.hi, + }, + }; + emu_point.into_value() + } + }, StarkNetTypeConcrete::Sha256StateHandle(_) => { - todo!("StarkNetTypeConcrete::Sha256StateHandle") + let raw_data = value_ptr.cast::>().read().read(); + let data = raw_data.into_iter().map(Value::U32).collect_vec(); + Value::Struct(data) } _ => unreachable!(), }, - CoreTypeConcrete::Bytes31(_) => todo!("CoreTypeConcrete::Bytes31"), + CoreTypeConcrete::Bytes31(_) => { + let original_data: [u8; 31] = value_ptr.cast().read(); + let mut data = [0u8; 32]; + for (i, v) in original_data.into_iter().enumerate() { + data[i] = v + } + + Value::Bytes31(Felt::from_bytes_le(&data)) + } } } + + #[repr(C, align(16))] + pub struct Secp256Point { + pub x: U256, + pub y: U256, + pub is_infinity: bool, + } + + #[repr(C, align(16))] + pub struct U256 { + pub lo: u128, + pub hi: u128, + } } diff --git a/scripts/bench-hyperfine.sh b/scripts/bench-hyperfine.sh index 47e9453c6..180120c79 100755 --- a/scripts/bench-hyperfine.sh +++ b/scripts/bench-hyperfine.sh @@ -90,7 +90,7 @@ run_bench() { -o "$OUTPUT_DIR/$base_name-march-native" \ >> /dev/stderr - CAIRO_NATIVE_RUNTIME_LIBDIR="$ROOT_DIR/target/release" hyperfine \ + hyperfine \ --warmup 3 \ --export-markdown "$OUTPUT_DIR/$base_name.md" \ --export-json "$OUTPUT_DIR/$base_name.json" \ diff --git a/src/arch.rs b/src/arch.rs index 45d91631e..6017e3158 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -1,7 +1,8 @@ use crate::{ error, - starknet::{ArrayAbi, U256}, + starknet::{ArrayAbi, Secp256k1Point, Secp256r1Point}, types::TypeBuilder, + utils::libc_malloc, values::Value, }; use bumpalo::Bump; @@ -25,9 +26,9 @@ pub trait AbiArgument { fn to_bytes(&self, buffer: &mut Vec) -> Result<(), error::Error>; } -/// A wrapper that implements `AbiArgument` for `JitValue`s. It contains all the required stuff to -/// serialize all possible `JitValue`s. -pub struct JitValueWithInfoWrapper<'a> { +/// A wrapper that implements `AbiArgument` for `Value`s. It contains all the required stuff to +/// serialize all possible `Value`s. +pub struct ValueWithInfoWrapper<'a> { pub value: &'a Value, pub type_id: &'a ConcreteTypeId, pub info: &'a CoreTypeConcrete, @@ -36,12 +37,12 @@ pub struct JitValueWithInfoWrapper<'a> { pub registry: &'a ProgramRegistry, } -impl<'a> JitValueWithInfoWrapper<'a> { +impl<'a> ValueWithInfoWrapper<'a> { fn map<'b>( &'b self, value: &'b Value, type_id: &'b ConcreteTypeId, - ) -> Result, error::Error> + ) -> Result, error::Error> where 'b: 'a, { @@ -55,7 +56,7 @@ impl<'a> JitValueWithInfoWrapper<'a> { } } -impl<'a> AbiArgument for JitValueWithInfoWrapper<'a> { +impl<'a> AbiArgument for ValueWithInfoWrapper<'a> { fn to_bytes(&self, buffer: &mut Vec) -> Result<(), error::Error> { match (self.value, self.info) { (value, CoreTypeConcrete::Box(info)) => { @@ -63,7 +64,7 @@ impl<'a> AbiArgument for JitValueWithInfoWrapper<'a> { let layout = self.registry.get_type(&info.ty)?.layout(self.registry)?; let heap_ptr = unsafe { - let heap_ptr = libc::malloc(layout.size()); + let heap_ptr = libc_malloc(layout.size()); libc::memcpy(heap_ptr, ptr.as_ptr().cast(), layout.size()); heap_ptr }; @@ -78,7 +79,7 @@ impl<'a> AbiArgument for JitValueWithInfoWrapper<'a> { let layout = self.registry.get_type(&info.ty)?.layout(self.registry)?; let heap_ptr = unsafe { - let heap_ptr = libc::malloc(layout.size()); + let heap_ptr = libc_malloc(layout.size()); libc::memcpy(heap_ptr, ptr.as_ptr().cast(), layout.size()); heap_ptr }; @@ -150,22 +151,20 @@ impl<'a> AbiArgument for JitValueWithInfoWrapper<'a> { .to_bytes(buffer)? } ( - Value::Secp256K1Point { x, y }, + Value::Secp256K1Point(Secp256k1Point { x, y, is_infinity }), CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::Secp256Point( Secp256PointTypeConcrete::K1(_), )), ) | ( - Value::Secp256R1Point { x, y }, + Value::Secp256R1Point(Secp256r1Point { x, y, is_infinity }), CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::Secp256Point( Secp256PointTypeConcrete::R1(_), )), ) => { - let x = U256 { lo: x.0, hi: x.1 }; - let y = U256 { lo: y.0, hi: y.1 }; - x.to_bytes(buffer)?; y.to_bytes(buffer)?; + is_infinity.to_bytes(buffer)?; } (Value::Sint128(value), CoreTypeConcrete::Sint128(_)) => value.to_bytes(buffer)?, (Value::Sint16(value), CoreTypeConcrete::Sint16(_)) => value.to_bytes(buffer)?, diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs index a58925244..b99d878a7 100644 --- a/src/arch/aarch64.rs +++ b/src/arch/aarch64.rs @@ -18,6 +18,18 @@ fn align_to(buffer: &mut Vec, align: usize) { buffer.resize(buffer.len().next_multiple_of(align), 0); } +impl AbiArgument for bool { + fn to_bytes(&self, buffer: &mut Vec) -> Result<(), Error> { + if buffer.len() < 64 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(1).align()); + buffer.push((*self) as u8); + } + Ok(()) + } +} + impl AbiArgument for u8 { fn to_bytes(&self, buffer: &mut Vec) -> Result<(), Error> { if buffer.len() < 64 { diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs index 59c525a9c..e00e12bcf 100644 --- a/src/arch/x86_64.rs +++ b/src/arch/x86_64.rs @@ -18,6 +18,18 @@ fn align_to(buffer: &mut Vec, align: usize) { buffer.resize(buffer.len().next_multiple_of(align), 0); } +impl AbiArgument for bool { + fn to_bytes(&self, buffer: &mut Vec) -> Result<(), Error> { + if buffer.len() < 48 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(1).align()); + buffer.push((*self) as u8); + } + Ok(()) + } +} + impl AbiArgument for u8 { fn to_bytes(&self, buffer: &mut Vec) -> Result<(), Error> { if buffer.len() < 48 { diff --git a/src/bin/cairo-native-run.rs b/src/bin/cairo-native-run.rs index 65eb96ecf..c850c4a29 100644 --- a/src/bin/cairo-native-run.rs +++ b/src/bin/cairo-native-run.rs @@ -108,7 +108,7 @@ fn main() -> anyhow::Result<()> { #[cfg(feature = "with-trace-dump")] { use cairo_lang_sierra::program_registry::ProgramRegistry; - use cairo_native::TypeBuilder; + use cairo_native::types::TypeBuilder; use cairo_native_runtime::trace_dump::{TraceDump, TRACE_DUMP}; TRACE_DUMP.lock().unwrap().insert( diff --git a/src/bin/utils/mod.rs b/src/bin/utils/mod.rs index 0bbf59825..fc145c5b1 100644 --- a/src/bin/utils/mod.rs +++ b/src/bin/utils/mod.rs @@ -4,7 +4,11 @@ use anyhow::bail; use cairo_lang_runner::{casm_run::format_next_item, RunResultValue}; use cairo_lang_sierra::program::{Function, Program}; -use cairo_native::{execution_result::ExecutionResult, Value}; +use cairo_native::{ + execution_result::ExecutionResult, + starknet::{Secp256k1Point, Secp256r1Point}, + Value, +}; use clap::ValueEnum; use itertools::Itertools; use starknet_types_core::felt::Felt; @@ -159,11 +163,19 @@ fn jitvalue_to_felt(value: &Value) -> Vec { Value::EcState(a, b, c, d) => { vec![*a, *b, *c, *d] } - Value::Secp256K1Point { x, y } => { - vec![x.0.into(), x.1.into(), y.0.into(), y.1.into()] + Value::Secp256K1Point(Secp256k1Point { + x, + y, + is_infinity: _, + }) => { + vec![x.lo.into(), x.hi.into(), y.lo.into(), y.hi.into()] } - Value::Secp256R1Point { x, y } => { - vec![x.0.into(), x.1.into(), y.0.into(), y.1.into()] + Value::Secp256R1Point(Secp256r1Point { + x, + y, + is_infinity: _, + }) => { + vec![x.lo.into(), x.hi.into(), y.lo.into(), y.hi.into()] } Value::Null => vec![0.into()], } @@ -550,10 +562,9 @@ mod tests { #[test] fn test_jitvalue_to_felt_secp256_k1_point() { assert_eq!( - jitvalue_to_felt(&Value::Secp256K1Point { - x: (1, 2), - y: (3, 4) - }), + jitvalue_to_felt(&Value::Secp256K1Point(Secp256k1Point::new( + 1, 2, 3, 4, false + ))), vec![Felt::ONE, Felt::TWO, Felt::THREE, Felt::from(4)] ); } @@ -561,10 +572,9 @@ mod tests { #[test] fn test_jitvalue_to_felt_secp256_r1_point() { assert_eq!( - jitvalue_to_felt(&Value::Secp256R1Point { - x: (1, 2), - y: (3, 4) - }), + jitvalue_to_felt(&Value::Secp256R1Point(Secp256r1Point::new( + 1, 2, 3, 4, false + ))), vec![Felt::ONE, Felt::TWO, Felt::THREE, Felt::from(4)] ); } diff --git a/src/compiler.rs b/src/compiler.rs index 52a337d6e..125252f08 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -74,7 +74,7 @@ use melior::{ arith::CmpiPredicate, cf, func, index, llvm::{self, LoadStoreOptions}, - memref, + memref, ods, }, ir::{ attribute::{ @@ -136,6 +136,30 @@ pub fn compile( } } + { + // Add the builtin_costs global. + // We always add it because symbol look up otherwise can panic. + let region = Region::new(); + let location = Location::unknown(context); + let block = region.append_block(Block::new(&[])); + let value = block.append_op_result( + ods::llvm::mlir_zero(context, llvm::r#type::pointer(context, 0), location).into(), + )?; + block.append_operation(melior::dialect::llvm::r#return(Some(value), location)); + + module.body().append_operation( + ods::llvm::mlir_global( + context, + region, + TypeAttribute::new(llvm::r#type::pointer(context, 0)), + StringAttribute::new(context, "builtin_costs"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + location, + ) + .into(), + ); + } + // Sierra programs have the following structure: // 1. Type declarations, one per line. // 2. Libfunc declarations, one per line. @@ -452,7 +476,7 @@ fn compile_func( initial_state, |statement_idx, mut state| { if let Some(gas_metadata) = metadata.get::() { - let gas_cost = gas_metadata.get_gas_cost_for_statement(statement_idx); + let gas_cost = gas_metadata.get_gas_costs_for_statement(statement_idx); metadata.remove::(); metadata.insert(GasCost(gas_cost)); } @@ -945,12 +969,16 @@ fn compile_func( &[ ( Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "public").into(), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ( + Identifier::new(context, "CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), ), - // ( - // Identifier::new(context, "CConv"), - // Attribute::parse(context, "#llvm.cconv").unwrap(), - // ), ], Location::fused( context, @@ -1374,10 +1402,10 @@ fn generate_entry_point_wrapper<'c>( Identifier::new(context, "callee"), FlatSymbolRefAttribute::new(context, private_symbol).into(), ), - // ( - // Identifier::new(context, "CConv"), - // Attribute::parse(context, "#llvm.cconv").unwrap(), - // ), + ( + Identifier::new(context, "CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), ]) .add_operands(&args) .add_results(&[llvm::r#type::r#struct(context, ret_types, false)]) @@ -1408,6 +1436,14 @@ fn generate_entry_point_wrapper<'c>( Identifier::new(context, "sym_visibility"), StringAttribute::new(context, "public").into(), ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ( + Identifier::new(context, "llvm.CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), ( Identifier::new(context, "llvm.emit_c_interface"), Attribute::unit(context), diff --git a/src/error.rs b/src/error.rs index c58b2fa57..2cc503d07 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,6 +69,9 @@ pub enum Error { #[error("integer conversion failed")] IntegerConversion, + #[error("missing BuiltinCosts global symbol, should never happen, this is a bug")] + MissingBuiltinCostsSymbol, + #[error(transparent)] IoError(#[from] std::io::Error), diff --git a/src/executor.rs b/src/executor.rs index d2127a8a9..f88e87735 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,12 +5,12 @@ pub use self::{aot::AotNativeExecutor, contract::AotContractExecutor, jit::JitNativeExecutor}; use crate::{ - arch::{AbiArgument, JitValueWithInfoWrapper}, + arch::{AbiArgument, ValueWithInfoWrapper}, error::Error, execution_result::{BuiltinStats, ExecutionResult}, starknet::{handler::StarknetSyscallHandlerCallbacks, StarknetSyscallHandler}, types::TypeBuilder, - utils::RangeExt, + utils::{libc_free, BuiltinCosts, RangeExt}, values::Value, }; use bumpalo::Bump; @@ -69,6 +69,7 @@ extern "C" { fn invoke_dynamic( registry: &ProgramRegistry, function_ptr: *const c_void, + builtin_costs_ptr: Option<*mut c_void>, function_signature: &FunctionSignature, args: &[Value], gas: u128, @@ -141,6 +142,15 @@ fn invoke_dynamic( previous_syscall_handler }); + // Order matters, for the libfunc impl + let builtin_costs: [u64; 7] = BuiltinCosts::default().into(); + + if let Some(builtin_costs_ptr) = builtin_costs_ptr { + unsafe { + *builtin_costs_ptr.cast() = builtin_costs.as_ptr(); + } + } + // Generate argument list. let mut iter = args.iter(); for item in function_signature.param_types.iter().filter_map(|type_id| { @@ -166,8 +176,15 @@ fn invoke_dynamic( (syscall_handler as *mut StarknetSyscallHandlerCallbacks<_>) .to_bytes(&mut invoke_data)?; } + CoreTypeConcrete::BuiltinCosts(_) => { + if let Some(builtin_costs_ptr) = builtin_costs_ptr { + builtin_costs_ptr.to_bytes(&mut invoke_data)?; + } else { + (builtin_costs.as_ptr()).to_bytes(&mut invoke_data)?; + } + } type_info if type_info.is_builtin() => 0u64.to_bytes(&mut invoke_data)?, - type_info => JitValueWithInfoWrapper { + type_info => ValueWithInfoWrapper { value: iter.next().unwrap(), type_id, info: type_info, @@ -249,26 +266,38 @@ fn invoke_dynamic( }, _ if type_info.is_builtin() => { if !type_info.is_zst(registry)? { - let value = match &mut return_ptr { - Some(return_ptr) => unsafe { *read_value::(return_ptr) }, - None => ret_registers[0], - } as usize; - - match type_info { - CoreTypeConcrete::Bitwise(_) => builtin_stats.bitwise = value, - CoreTypeConcrete::EcOp(_) => builtin_stats.ec_op = value, - CoreTypeConcrete::RangeCheck(_) => builtin_stats.range_check = value, - CoreTypeConcrete::Pedersen(_) => builtin_stats.pedersen = value, - CoreTypeConcrete::Poseidon(_) => builtin_stats.poseidon = value, - CoreTypeConcrete::SegmentArena(_) => builtin_stats.segment_arena = value, - CoreTypeConcrete::RangeCheck96(_) => builtin_stats.range_check_96 = value, - CoreTypeConcrete::Circuit(CircuitTypeConcrete::AddMod(_)) => { - builtin_stats.circuit_add = value - } - CoreTypeConcrete::Circuit(CircuitTypeConcrete::MulMod(_)) => { - builtin_stats.circuit_mul = value + if let CoreTypeConcrete::BuiltinCosts(_) = type_info { + // todo: should we use this value? + let _value = match &mut return_ptr { + Some(return_ptr) => unsafe { *read_value::<*mut u64>(return_ptr) }, + None => ret_registers[0] as *mut u64, + }; + } else { + let value = match &mut return_ptr { + Some(return_ptr) => unsafe { *read_value::(return_ptr) }, + None => ret_registers[0], + } as usize; + + match type_info { + CoreTypeConcrete::Bitwise(_) => builtin_stats.bitwise = value, + CoreTypeConcrete::EcOp(_) => builtin_stats.ec_op = value, + CoreTypeConcrete::RangeCheck(_) => builtin_stats.range_check = value, + CoreTypeConcrete::Pedersen(_) => builtin_stats.pedersen = value, + CoreTypeConcrete::Poseidon(_) => builtin_stats.poseidon = value, + CoreTypeConcrete::SegmentArena(_) => { + builtin_stats.segment_arena = value + } + CoreTypeConcrete::RangeCheck96(_) => { + builtin_stats.range_check_96 = value + } + CoreTypeConcrete::Circuit(CircuitTypeConcrete::AddMod(_)) => { + builtin_stats.circuit_add = value + } + CoreTypeConcrete::Circuit(CircuitTypeConcrete::MulMod(_)) => { + builtin_stats.circuit_mul = value + } + _ => unreachable!("{type_id:?}"), } - _ => unreachable!("{type_id:?}"), } } } @@ -298,6 +327,9 @@ fn invoke_dynamic( debug_name: None, }); + #[cfg(feature = "with-mem-tracing")] + crate::utils::mem_tracing::report_stats(); + Ok(ExecutionResult { remaining_gas, return_value, @@ -336,7 +368,7 @@ fn parse_result( CoreTypeConcrete::Box(info) => unsafe { let ptr = return_ptr.unwrap_or(NonNull::new_unchecked(ret_registers[0] as *mut ())); let value = Value::from_ptr(ptr, &info.ty, registry)?; - libc::free(ptr.cast().as_ptr()); + libc_free(ptr.cast().as_ptr()); Ok(value) }, CoreTypeConcrete::EcPoint(_) | CoreTypeConcrete::EcState(_) => { @@ -358,11 +390,13 @@ fn parse_result( return Err(Error::ParseAttributeError); #[cfg(target_arch = "aarch64")] - Ok(Value::Felt252( - starknet_types_core::felt::Felt::from_bytes_le(unsafe { - std::mem::transmute::<&[u64; 4], &[u8; 32]>(&ret_registers) - }), - )) + Ok(Value::Felt252({ + let data = unsafe { + std::mem::transmute::<&mut [u64; 4], &mut [u8; 32]>(&mut ret_registers) + }; + data[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + starknet_types_core::felt::Felt::from_bytes_le(data) + })) } }, CoreTypeConcrete::Bytes31(_) => match return_ptr { @@ -454,7 +488,7 @@ fn parse_result( } else { let ptr = NonNull::new_unchecked(ptr); let value = Value::from_ptr(ptr, &info.ty, registry)?; - libc::free(ptr.as_ptr().cast()); + libc_free(ptr.as_ptr().cast()); Ok(value) } }, @@ -476,6 +510,12 @@ fn parse_result( } }; + // Filter out bits that are not part of the enum's tag. + let tag = tag + & 1usize + .wrapping_shl(info.variants.len().next_power_of_two().trailing_zeros()) + .wrapping_sub(1); + ( tag, Ok(unsafe { diff --git a/src/executor/aot.rs b/src/executor/aot.rs index 31a05bc30..bff56301c 100644 --- a/src/executor/aot.rs +++ b/src/executor/aot.rs @@ -81,6 +81,7 @@ impl AotNativeExecutor { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), + self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id), args, available_gas, @@ -103,6 +104,7 @@ impl AotNativeExecutor { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), + self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id), args, available_gas, @@ -125,6 +127,7 @@ impl AotNativeExecutor { ContractExecutionResult::from_execution_result(super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), + self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id), &[Value::Struct { fields: vec![Value::Array( @@ -152,6 +155,15 @@ impl AotNativeExecutor { } } + pub fn find_symbol_ptr(&self, name: &str) -> Option<*mut c_void> { + unsafe { + self.library + .get::<*mut ()>(name.as_bytes()) + .ok() + .map(|x| x.into_raw().into_raw()) + } + } + fn extract_signature(&self, function_id: &FunctionId) -> &FunctionSignature { &self.registry.get_function(function_id).unwrap().signature } diff --git a/src/executor/contract.rs b/src/executor/contract.rs index 5d8e3db51..e6bea6ff2 100644 --- a/src/executor/contract.rs +++ b/src/executor/contract.rs @@ -34,13 +34,16 @@ use crate::{ arch::AbiArgument, context::NativeContext, - error::Result, + error::{Error, Result}, execution_result::{BuiltinStats, ContractExecutionResult}, executor::invoke_trampoline, module::NativeModule, starknet::{handler::StarknetSyscallHandlerCallbacks, StarknetSyscallHandler}, types::TypeBuilder, - utils::{decode_error_message, generate_function_name, get_integer_layout}, + utils::{ + decode_error_message, generate_function_name, get_integer_layout, libc_free, libc_malloc, + BuiltinCosts, + }, OptLevel, }; use bumpalo::Bump; @@ -71,19 +74,30 @@ use tempfile::NamedTempFile; #[educe(Debug)] pub struct AotContractExecutor { #[educe(Debug(ignore))] - library: Arc, + pub library: Arc, path: PathBuf, is_temp_path: bool, - entry_points_info: BTreeMap, + contract_info: ContractInfo, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct ContractInfo { + pub version: ContractInfoVersion, + pub entry_points: BTreeMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub enum ContractInfoVersion { + Version0, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -struct EntryPointInfo { - builtins: Vec, +pub struct EntryPointInfo { + pub builtins: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -enum BuiltinType { +pub enum BuiltinType { Bitwise, EcOp, RangeCheck, @@ -95,6 +109,7 @@ enum BuiltinType { CircuitMul, Gas, System, + BuiltinCosts, } impl AotContractExecutor { @@ -132,6 +147,9 @@ impl AotContractExecutor { CoreTypeConcrete::RangeCheck(_) => builtins.push(BuiltinType::RangeCheck), CoreTypeConcrete::Pedersen(_) => builtins.push(BuiltinType::Pedersen), CoreTypeConcrete::Poseidon(_) => builtins.push(BuiltinType::Poseidon), + CoreTypeConcrete::BuiltinCosts(_) => { + builtins.push(BuiltinType::BuiltinCosts) + } CoreTypeConcrete::SegmentArena(_) => { builtins.push(BuiltinType::SegmentArena) } @@ -170,7 +188,10 @@ impl AotContractExecutor { library: Arc::new(unsafe { Library::new(&library_path)? }), path: library_path, is_temp_path: true, - entry_points_info: infos, + contract_info: ContractInfo { + version: ContractInfoVersion::Version0, + entry_points: infos, + }, }) } @@ -179,9 +200,9 @@ impl AotContractExecutor { let to = to.as_ref(); std::fs::copy(&self.path, to)?; - let info = serde_json::to_string(&self.entry_points_info)?; + let contract_info = serde_json::to_string(&self.contract_info)?; let path = to.with_extension("json"); - std::fs::write(path, info)?; + std::fs::write(path, contract_info)?; self.path = to.to_path_buf(); self.is_temp_path = false; @@ -192,12 +213,12 @@ impl AotContractExecutor { /// Load the executor from an already compiled library with the additional info json file. pub fn load(library_path: &Path) -> Result { let info_str = std::fs::read_to_string(library_path.with_extension("json"))?; - let info: BTreeMap = serde_json::from_str(&info_str)?; + let contract_info: ContractInfo = serde_json::from_str(&info_str)?; Ok(Self { library: Arc::new(unsafe { Library::new(library_path)? }), path: library_path.to_path_buf(), is_temp_path: false, - entry_points_info: info, + contract_info, }) } @@ -207,16 +228,30 @@ impl AotContractExecutor { function_id: &FunctionId, args: &[Felt], gas: Option, + builtin_costs: Option, mut syscall_handler: impl StarknetSyscallHandler, ) -> Result { let arena = Bump::new(); let mut invoke_data = Vec::::new(); let function_ptr = self.find_function_ptr(function_id, true)?; + let builtin_costs_ptr = self + .find_symbol_ptr("builtin_costs") + .ok_or_else(|| Error::MissingBuiltinCostsSymbol)?; + + let builtin_costs = builtin_costs.unwrap_or_default(); + let builtin_costs: [u64; 7] = builtin_costs.into(); + + unsafe { + *builtin_costs_ptr.cast() = builtin_costs.as_ptr(); + } // it can vary from contract to contract thats why we need to store/ load it. // substract 2, which are the gas and syscall builtin - let num_builtins = self.entry_points_info[&function_id.id].builtins.len() - 2; + let num_builtins = self.contract_info.entry_points[&function_id.id] + .builtins + .len() + - 2; // There is always a return ptr because contracts always return more than 1 thing (builtin counters, syscall, enum) let return_ptr = arena.alloc_layout(unsafe { @@ -229,12 +264,15 @@ impl AotContractExecutor { let mut syscall_handler = StarknetSyscallHandlerCallbacks::new(&mut syscall_handler); - for b in &self.entry_points_info[&function_id.id].builtins { + for b in &self.contract_info.entry_points[&function_id.id].builtins { match b { BuiltinType::Gas => { let gas = gas.unwrap_or(0); gas.to_bytes(&mut invoke_data)?; } + BuiltinType::BuiltinCosts => { + builtin_costs_ptr.to_bytes(&mut invoke_data)?; + } BuiltinType::System => { (&mut syscall_handler as *mut StarknetSyscallHandlerCallbacks<_>) .to_bytes(&mut invoke_data)?; @@ -246,7 +284,7 @@ impl AotContractExecutor { } let felt_layout = get_integer_layout(252).pad_to_align(); - let ptr: *mut () = unsafe { libc::malloc(felt_layout.size() * args.len()).cast() }; + let ptr: *mut () = unsafe { libc_malloc(felt_layout.size() * args.len()).cast() }; let len: u32 = args.len().try_into().unwrap(); ptr.to_bytes(&mut invoke_data)?; @@ -309,7 +347,7 @@ impl AotContractExecutor { let return_ptr = &mut return_ptr.cast(); - for b in &self.entry_points_info[&function_id.id].builtins { + for b in &self.contract_info.entry_points[&function_id.id].builtins { match b { BuiltinType::Gas => { remaining_gas = unsafe { *read_value::(return_ptr) }; @@ -318,6 +356,11 @@ impl AotContractExecutor { let ptr = return_ptr.cast::<*mut ()>(); *return_ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().add(1)).cast() }; } + BuiltinType::BuiltinCosts => { + let ptr = return_ptr.cast::<*mut ()>(); + *return_ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().add(1)).cast() }; + // ptr holds the builtin costs, but they dont change, so its of no use, but we read to advance the ptr. + } x => { let value = unsafe { *read_value::(return_ptr) } as usize; @@ -333,6 +376,7 @@ impl AotContractExecutor { BuiltinType::CircuitMul => builtin_stats.circuit_mul = value, BuiltinType::Gas => {} BuiltinType::System => {} + BuiltinType::BuiltinCosts => {} } } } @@ -353,6 +397,8 @@ impl AotContractExecutor { }; let tag = *unsafe { enum_ptr.cast::().as_ref() } as usize; + let tag = tag & 0x01; // Filter out bits that are not part of the enum's tag. + // layout of both enum variants, both are a array of felts let value_layout = unsafe { Layout::from_size_align_unchecked(24, 8) }; let value_ptr = unsafe { @@ -380,14 +426,15 @@ impl AotContractExecutor { for i in 0..num_elems { // safe to create a NonNull because if the array has elements, the data_ptr can't be null. let cur_elem_ptr = NonNull::new(unsafe { data_ptr.byte_add(elem_stride * i) }).unwrap(); - let data = unsafe { cur_elem_ptr.cast::<[u8; 32]>().as_ref() }; + let data = unsafe { cur_elem_ptr.cast::<[u8; 32]>().as_mut() }; + data[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). let data = Felt::from_bytes_le_slice(data); array_value.push(data); } if !array_ptr.is_null() { - unsafe { libc::free(array_ptr.cast()) }; + unsafe { libc_free(array_ptr.cast()) }; } let mut error_msg = None; @@ -404,6 +451,9 @@ impl AotContractExecutor { error_msg = Some(str_error); } + #[cfg(feature = "with-mem-tracing")] + crate::utils::mem_tracing::report_stats(); + Ok(ContractExecutionResult { remaining_gas, failure_flag: tag != 0, @@ -428,6 +478,15 @@ impl AotContractExecutor { .into_raw() }) } + + pub fn find_symbol_ptr(&self, name: &str) -> Option<*mut c_void> { + unsafe { + self.library + .get::<*mut ()>(name.as_bytes()) + .ok() + .map(|x| x.into_raw().into_raw()) + } + } } impl Drop for AotContractExecutor { @@ -511,6 +570,7 @@ mod tests { entrypoint_function_id, &[2.into()], Some(u64::MAX as u128), + None, &mut StubSyscallHandler::default(), ) .unwrap(); @@ -536,6 +596,7 @@ mod tests { entrypoint_function_id, &[], Some(u64::MAX as u128), + None, &mut StubSyscallHandler::default(), ) .unwrap(); diff --git a/src/executor/jit.rs b/src/executor/jit.rs index 55bfafcbe..4b50ce113 100644 --- a/src/executor/jit.rs +++ b/src/executor/jit.rs @@ -79,6 +79,7 @@ impl<'m> JitNativeExecutor<'m> { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), + self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id).unwrap(), args, available_gas, @@ -102,6 +103,7 @@ impl<'m> JitNativeExecutor<'m> { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), + self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id).unwrap(), args, available_gas, @@ -124,6 +126,7 @@ impl<'m> JitNativeExecutor<'m> { ContractExecutionResult::from_execution_result(super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), + self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id).unwrap(), &[Value::Struct { fields: vec![Value::Array( @@ -145,6 +148,16 @@ impl<'m> JitNativeExecutor<'m> { self.engine.lookup(&function_name) as *mut c_void } + pub fn find_symbol_ptr(&self, name: &str) -> Option<*mut c_void> { + let ptr = self.engine.lookup(name) as *mut c_void; + + if ptr.is_null() { + None + } else { + Some(ptr) + } + } + fn extract_signature(&self, function_id: &FunctionId) -> Option<&FunctionSignature> { self.program_registry() .get_function(function_id) diff --git a/src/ffi.rs b/src/ffi.rs index 0e6b563ce..09cccbbcf 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -29,6 +29,7 @@ use melior::ir::{Module, Type, TypeLike}; use mlir_sys::{mlirLLVMStructTypeGetElementType, mlirTranslateModuleToLLVMIR}; use std::{ borrow::Cow, + env, ffi::{CStr, CString}, io::Write, mem::MaybeUninit, @@ -143,7 +144,7 @@ pub fn module_to_object(module: &Module<'_>, opt_level: OptLevel) -> Result LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault, OptLevel::Aggressive => LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive, }, - LLVMRelocMode::LLVMRelocDynamicNoPic, + LLVMRelocMode::LLVMRelocPIC, LLVMCodeModel::LLVMCodeModelDefault, ); @@ -152,8 +153,11 @@ pub fn module_to_object(module: &Module<'_>, opt_level: OptLevel) -> Result 0, OptLevel::Less => 1, - OptLevel::Default => 1, // todo: change once slp-vectorizer pass is fixed on llvm - OptLevel::Aggressive => 1, // https://github.com/llvm/llvm-project/issues/107198 + // slp-vectorizer pass did cause some issues, but after the change + // on function attributes it seems to not trigger them anymore. + // https://github.com/llvm/llvm-project/issues/107198 + OptLevel::Default => 2, + OptLevel::Aggressive => 3, }; let passes = CString::new(format!("default")).unwrap(); let error = LLVMRunPasses(llvm_module, passes.as_ptr(), machine, opts); @@ -219,6 +223,28 @@ pub fn object_to_shared_lib(object: &[u8], output_filename: &Path) -> Result<()> } } + let runtime_library_path = if let Ok(extra_dir) = std::env::var("CAIRO_NATIVE_RUNTIME_LIBRARY") + { + let path = Path::new(&extra_dir); + if path.is_absolute() { + extra_dir + } else { + let mut absolute_path = env::current_dir() + .expect("Failed to get the current directory") + .join(path); + absolute_path = absolute_path + .canonicalize() + .expect("Failed to cannonicalize path"); + String::from( + absolute_path + .to_str() + .expect("Absolute path contains non-utf8 characters"), + ) + } + } else { + String::from("libcairo_native_runtime.a") + }; + let args: Vec> = { #[cfg(target_os = "macos")] { @@ -236,14 +262,9 @@ pub fn object_to_shared_lib(object: &[u8], output_filename: &Path) -> Result<()> "-o".into(), Cow::from(output_path), "-lSystem".into(), + Cow::from(runtime_library_path), ]); - if let Ok(extra_dir) = std::env::var("CAIRO_NATIVE_RUNTIME_LIBRARY") { - args.extend([Cow::from(extra_dir)]); - } else { - args.extend(["libcairo_native_runtime.a".into()]); - } - args } #[cfg(target_os = "linux")] @@ -261,14 +282,9 @@ pub fn object_to_shared_lib(object: &[u8], output_filename: &Path) -> Result<()> Cow::from(output_path), "-lc".into(), Cow::from(file_path), + Cow::from(runtime_library_path), ]); - if let Ok(extra_dir) = std::env::var("CAIRO_NATIVE_RUNTIME_LIBRARY") { - args.extend([Cow::from(extra_dir)]); - } else { - args.extend(["libcairo_native_runtime.a".into()]); - } - args } #[cfg(target_os = "windows")] diff --git a/src/lib.rs b/src/lib.rs index 8705d6c4b..0d75440dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ pub use self::{ }; #[cfg(feature = "with-trace-dump")] -pub use {self::types::TypeBuilder, cairo_native_runtime as runtime}; +pub use cairo_native_runtime as runtime; mod arch; pub mod cache; @@ -27,11 +27,11 @@ pub mod error; pub mod execution_result; pub mod executor; mod ffi; -mod libfuncs; +pub mod libfuncs; pub mod metadata; pub mod module; pub mod starknet; pub mod starknet_stub; -mod types; +pub mod types; pub mod utils; mod values; diff --git a/src/libfuncs.rs b/src/libfuncs.rs index dcec662c2..6c2a2c665 100644 --- a/src/libfuncs.rs +++ b/src/libfuncs.rs @@ -250,7 +250,7 @@ impl LibfuncBuilder for CoreConcreteLibfunc { /// This helper is necessary because the statement following the current one may not have the same /// arguments as the results returned by the current statement. Because of that, a direct jump /// cannot be made and some processing is required. -pub(crate) struct LibfuncHelper<'ctx, 'this> +pub struct LibfuncHelper<'ctx, 'this> where 'this: 'ctx, { @@ -408,7 +408,7 @@ impl<'ctx, 'this> Deref for LibfuncHelper<'ctx, 'this> { } #[derive(Clone, Copy, Debug)] -pub(crate) enum BranchArg<'ctx, 'this> { +pub enum BranchArg<'ctx, 'this> { External(Value<'ctx, 'this>), Returned(usize), } diff --git a/src/libfuncs/array.rs b/src/libfuncs/array.rs index fe87547af..5d8e38edd 100644 --- a/src/libfuncs/array.rs +++ b/src/libfuncs/array.rs @@ -15,7 +15,7 @@ use crate::{ use cairo_lang_sierra::{ extensions::{ array::{ArrayConcreteLibfunc, ConcreteMultiPopLibfunc}, - core::{CoreLibfunc, CoreType}, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, lib_func::{SignatureAndTypeConcreteLibfunc, SignatureOnlyConcreteLibfunc}, ConcreteLibfunc, }, @@ -35,7 +35,6 @@ use melior::{ }, Context, }; -use std::ops::Deref; /// Select and call the correct libfunc builder function from the selector. pub fn build<'ctx, 'this>( @@ -380,6 +379,21 @@ pub fn build_len<'ctx, 'this>( let array_len = entry.append_op_result(arith::subi(array_end, array_start, location))?; + match metadata.get::() { + Some(drop_overrides_meta) + if drop_overrides_meta.is_overriden(&info.signature.param_signatures[0].ty) => + { + drop_overrides_meta.invoke_override( + context, + entry, + location, + &info.signature.param_signatures[0].ty, + entry.argument(0)?.into(), + )?; + } + _ => {} + } + entry.append_operation(helper.br(0, &[array_len], location)); Ok(()) } @@ -578,7 +592,18 @@ pub fn build_get<'ctx, 'this>( valid_block.append_operation(helper.br(0, &[range_check, target_ptr], location)); } + metadata + .get::() + .unwrap() + .invoke_override( + context, + error_block, + location, + &info.param_signatures()[1].ty, + value, + )?; error_block.append_operation(helper.br(1, &[range_check], location)); + Ok(()) } @@ -692,8 +717,105 @@ pub fn build_pop_front_consume<'ctx, 'this>( metadata: &mut MetadataStorage, info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { - // Equivalent to `array_pop_front_consume` for our purposes. - build_pop_front(context, registry, entry, location, helper, metadata, info) + if metadata.get::().is_none() { + metadata.insert(ReallocBindingsMeta::new(context, helper)); + } + + let array_ty = registry.build_type( + context, + helper, + registry, + metadata, + &info.param_signatures()[0].ty, + )?; + + let elem_ty = registry.get_type(&info.ty)?; + let elem_layout = elem_ty.layout(registry)?; + + let ptr_ty = crate::ffi::get_struct_field_type_at(&array_ty, 0); + let len_ty = crate::ffi::get_struct_field_type_at(&array_ty, 1); + + let value = entry.argument(0)?.into(); + + let array_start = entry.extract_value(context, location, value, len_ty, 1)?; + let array_end = entry.extract_value(context, location, value, len_ty, 2)?; + + let is_empty = entry.append_op_result(arith::cmpi( + context, + CmpiPredicate::Eq, + array_start, + array_end, + location, + ))?; + + let valid_block = helper.append_block(Block::new(&[])); + let empty_block = helper.append_block(Block::new(&[])); + entry.append_operation(cf::cond_br( + context, + is_empty, + empty_block, + valid_block, + &[], + &[], + location, + )); + + { + let ptr = valid_block.extract_value(context, location, value, ptr_ty, 0)?; + + let elem_size = valid_block.const_int(context, location, elem_layout.size(), 64)?; + let elem_offset = valid_block.append_op_result(arith::extui( + array_start, + IntegerType::new(context, 64).into(), + location, + ))?; + let elem_offset = + valid_block.append_op_result(arith::muli(elem_offset, elem_size, location))?; + let ptr = valid_block.append_op_result(llvm::get_element_ptr_dynamic( + context, + ptr, + &[elem_offset], + IntegerType::new(context, 8).into(), + llvm::r#type::pointer(context, 0), + location, + ))?; + + let target_ptr = valid_block.append_op_result( + ods::llvm::mlir_zero(context, pointer(context, 0), location).into(), + )?; + let target_ptr = valid_block.append_op_result(ReallocBindingsMeta::realloc( + context, target_ptr, elem_size, location, + ))?; + assert_nonnull( + context, + valid_block, + location, + target_ptr, + "realloc returned nullptr", + )?; + + valid_block.memcpy(context, location, ptr, target_ptr, elem_size); + + let k1 = valid_block.const_int(context, location, 1, 32)?; + let new_start = valid_block.append_op_result(arith::addi(array_start, k1, location))?; + let value = valid_block.insert_value(context, location, value, new_start, 1)?; + + valid_block.append_operation(helper.br(0, &[value, target_ptr], location)); + } + + metadata + .get::() + .unwrap() + .invoke_override( + context, + empty_block, + location, + &info.param_signatures()[0].ty, + value, + )?; + empty_block.append_operation(helper.br(1, &[], location)); + + Ok(()) } /// Generate MLIR operations for the `array_snapshot_pop_front` libfunc. @@ -1071,60 +1193,46 @@ pub fn build_slice<'ctx, 'this>( metadata: &mut MetadataStorage, info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { - if metadata.get::().is_none() { - metadata.insert(ReallocBindingsMeta::new(context, helper)); - } - let range_check = super::increment_builtin_counter(context, entry, location, entry.argument(0)?.into())?; - let array_ty = registry.build_type( - context, - helper, - registry, - metadata, - &info.param_signatures()[1].ty, - )?; - - let len_ty = crate::ffi::get_struct_field_type_at(&array_ty, 1); + let len_ty = IntegerType::new(context, 32).into(); let elem_ty = registry.get_type(&info.ty)?; let elem_layout = elem_ty.layout(registry)?; - let slice_since = entry.argument(2)?.into(); - let slice_length = entry.argument(3)?.into(); - - let slice_until = entry.append_op_result(arith::addi(slice_since, slice_length, location))?; - let array_start = entry.extract_value(context, location, entry.argument(1)?.into(), len_ty, 1)?; let array_end = entry.extract_value(context, location, entry.argument(1)?.into(), len_ty, 2)?; - let slice_since = entry.append_op_result(arith::addi(slice_since, array_start, location))?; - let slice_until = entry.append_op_result(arith::addi(slice_until, array_start, location))?; + let slice_start = entry.argument(2)?.into(); + let slice_len = entry.argument(3)?.into(); + let slice_end = entry.append_op_result(arith::addi(slice_start, slice_len, location))?; + let slice_start = entry.append_op_result(arith::addi(array_start, slice_start, location))?; + let slice_end = entry.append_op_result(arith::addi(array_start, slice_end, location))?; let lhs_bound = entry.append_op_result(arith::cmpi( context, CmpiPredicate::Uge, - slice_since, + slice_start, array_start, location, ))?; let rhs_bound = entry.append_op_result(arith::cmpi( context, CmpiPredicate::Ule, - slice_until, + slice_end, array_end, location, ))?; - let is_fully_contained = entry.append_op_result(arith::andi(lhs_bound, rhs_bound, location))?; + let is_valid = entry.append_op_result(arith::andi(lhs_bound, rhs_bound, location))?; let slice_block = helper.append_block(Block::new(&[])); let error_block = helper.append_block(Block::new(&[])); entry.append_operation(cf::cond_br( context, - is_fully_contained, + is_valid, slice_block, error_block, &[], @@ -1133,65 +1241,106 @@ pub fn build_slice<'ctx, 'this>( )); { - let elem_size = - slice_block.const_int(context, location, elem_layout.pad_to_align().size(), 64)?; - let dst_size = slice_block.append_op_result(arith::extui( - slice_length, - IntegerType::new(context, 64).into(), - location, - ))?; - let dst_size = slice_block.append_op_result(arith::muli(dst_size, elem_size, location))?; + let elem_ty = elem_ty.build(context, helper, registry, metadata, &info.ty)?; - let dst_ptr = slice_block.append_op_result( - ods::llvm::mlir_zero(context, pointer(context, 0), location).into(), - )?; - let dst_ptr = slice_block.append_op_result(ReallocBindingsMeta::realloc( - context, dst_ptr, dst_size, location, - ))?; + let value = entry.argument(1)?.into(); + let value = slice_block.insert_value(context, location, value, slice_start, 1)?; + let value = slice_block.insert_value(context, location, value, slice_end, 2)?; - // TODO: Find out if we need to clone stuff using the snapshot clone meta. - let src_offset = { - let slice_since = slice_block.append_op_result(arith::extui( - slice_since, + let elem_stride = + slice_block.const_int(context, location, elem_layout.pad_to_align().size(), 64)?; + let prepare = |value| { + let value = slice_block.append_op_result(arith::extui( + value, IntegerType::new(context, 64).into(), location, ))?; - - slice_block.append_op_result(arith::muli(slice_since, elem_size, location))? + slice_block.append_op_result(arith::muli(value, elem_stride, location)) }; - let src_ptr = slice_block.extract_value( + let ptr = slice_block.extract_value( context, location, entry.argument(1)?.into(), - pointer(context, 0), + llvm::r#type::pointer(context, 0), 0, )?; - let src_ptr = slice_block.append_op_result(llvm::get_element_ptr_dynamic( - context, - src_ptr, - &[src_offset], - IntegerType::new(context, 8).into(), - llvm::r#type::pointer(context, 0), - location, - ))?; + let make_region = |drop_overrides_meta: &DropOverridesMeta| { + let region = Region::new(); + let block = region.append_block(Block::new(&[( + IntegerType::new(context, 64).into(), + location, + )])); + + let value_ptr = block.append_op_result(llvm::get_element_ptr_dynamic( + context, + ptr, + &[block.argument(0)?.into()], + IntegerType::new(context, 8).into(), + llvm::r#type::pointer(context, 0), + location, + ))?; - slice_block.memcpy(context, location, src_ptr, dst_ptr, dst_size); + let value = block.load(context, location, value_ptr, elem_ty)?; + drop_overrides_meta.invoke_override(context, &block, location, &info.ty, value)?; - let k0 = slice_block.const_int_from_type(context, location, 0, len_ty)?; + block.append_operation(scf::r#yield(&[], location)); + Result::Ok(region) + }; + + let array_start = prepare(array_start)?; + let array_end = prepare(array_end)?; + let slice_start = prepare(slice_start)?; + let slice_end = prepare(slice_end)?; - let value = slice_block.append_op_result(llvm::undef(array_ty, location))?; - let value = slice_block.insert_values( + match metadata.get::() { + Some(drop_overrides_meta) if drop_overrides_meta.is_overriden(&info.ty) => { + slice_block.append_operation(scf::r#for( + array_start, + slice_start, + elem_stride, + make_region(drop_overrides_meta)?, + location, + )); + slice_block.append_operation(scf::r#for( + slice_end, + array_end, + elem_stride, + make_region(drop_overrides_meta)?, + location, + )); + } + _ => {} + }; + + slice_block.append_operation(helper.br(0, &[range_check, value], location)); + } + + { + registry.build_type( context, - location, - value, - &[dst_ptr, k0, slice_length, slice_length], + helper, + registry, + metadata, + &info.signature.param_signatures[1].ty, )?; - slice_block.append_operation(helper.br(0, &[range_check, value], location)); + // The following unwrap is unreachable because an array always has a drop implementation, + // which at this point is always inserted thanks to the `build_type()` just above. + metadata + .get::() + .unwrap() + .invoke_override( + context, + error_block, + location, + &info.signature.param_signatures[1].ty, + entry.argument(1)?.into(), + )?; + + error_block.append_operation(helper.br(1, &[range_check], location)); } - error_block.append_operation(helper.br(1, &[range_check], location)); Ok(()) } @@ -1206,7 +1355,6 @@ pub fn build_span_from_tuple<'ctx, 'this>( info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { // tuple to array span (t,t,t) -> &[t,t,t] - if metadata.get::().is_none() { metadata.insert(ReallocBindingsMeta::new(context, helper)); } @@ -1322,48 +1470,59 @@ pub fn build_tuple_from_span<'ctx, 'this>( metadata: &mut MetadataStorage, info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { - // Libfunc Signature: - // - // (Snapshot>) -> Box> + // Tasks: + // - Check if sizes match. + // - If they do not match, jump to branch [1]. + // - If they do match: + // - If start == 0 && capacity == len -> reuse the pointer + // - Otherwise, realloc + memcpy + free. if metadata.get::().is_none() { metadata.insert(ReallocBindingsMeta::new(context, helper)); } - // Get type information - - let elem_core_ty = registry.get_type(&info.signature.param_signatures[0].ty)?; - let elem_layout = elem_core_ty.layout(registry)?; - let elem_stride = elem_layout.pad_to_align().size(); - let tuple_len = registry - .get_type(&info.ty)? - .fields() - .expect("should be a struct (ergo, has fields)") - .len(); + let array_ty = registry.get_type(&info.signature.param_signatures[0].ty)?; + let (elem_id, elem_ty) = match array_ty { + CoreTypeConcrete::Array(info) => (&info.ty, registry.get_type(&info.ty)?), + CoreTypeConcrete::Snapshot(info) => match registry.get_type(&info.ty)? { + CoreTypeConcrete::Array(info) => (&info.ty, registry.get_type(&info.ty)?), + _ => unreachable!(), + }, + _ => unreachable!(), + }; + let elem_layout = elem_ty.layout(registry)?; - let u32_ty = IntegerType::new(context, 32).into(); + let array_start = entry.extract_value( + context, + location, + entry.argument(0)?.into(), + IntegerType::new(context, 32).into(), + 1, + )?; + let array_end = entry.extract_value( + context, + location, + entry.argument(0)?.into(), + IntegerType::new(context, 32).into(), + 2, + )?; - // Get array information + let array_len = entry.append_op_result(arith::subi(array_end, array_start, location))?; + let (tuple_len, tuple_len_val) = { + let fields = registry.get_type(&info.ty)?.fields().unwrap(); + assert!(fields.iter().all(|f| f.id == elem_id.id)); - let array_value = entry.argument(0)?.into(); - let array_ptr = entry.extract_value(context, location, array_value, pointer(context, 0), 0)?; - let array_start = entry.extract_value(context, location, array_value, u32_ty, 1)?; - let array_end = entry.extract_value(context, location, array_value, u32_ty, 2)?; - let array_capacity = entry.extract_value(context, location, array_value, u32_ty, 3)?; - - // Check if conversion is valid - // - // if array.end - array.start != tuple_len { - // return err; - // } + ( + entry.const_int(context, location, fields.len(), 32)?, + fields.len(), + ) + }; - let array_len = entry.append_op_result(arith::subi(array_end, array_start, location))?; - let tuple_len_value = entry.const_int(context, location, tuple_len, 32)?; - let array_len_matches = entry.append_op_result(arith::cmpi( + let len_matches = entry.append_op_result(arith::cmpi( context, CmpiPredicate::Eq, array_len, - tuple_len_value, + tuple_len, location, ))?; @@ -1371,7 +1530,7 @@ pub fn build_tuple_from_span<'ctx, 'this>( let block_err = helper.append_block(Block::new(&[])); entry.append_operation(cf::cond_br( context, - array_len_matches, + len_matches, block_ok, block_err, &[], @@ -1379,108 +1538,132 @@ pub fn build_tuple_from_span<'ctx, 'this>( location, )); - // Check if pointer can be passed through, that is - // if array.start == 0 && array.capacity == tuple_len - - let is_pointer_passthrough = { + { let k0 = block_ok.const_int(context, location, 0, 32)?; - let array_since_is_zero = block_ok.append_op_result(arith::cmpi( + let starts_at_zero = block_ok.append_op_result(arith::cmpi( context, CmpiPredicate::Eq, array_start, k0, location, ))?; - let array_cap_matches = block_ok.append_op_result(arith::cmpi( + + let array_cap = block_ok.extract_value( + context, + location, + entry.argument(0)?.into(), + IntegerType::new(context, 32).into(), + 3, + )?; + let capacity_matches = block_ok.append_op_result(arith::cmpi( context, CmpiPredicate::Eq, - array_capacity, - tuple_len_value, + array_cap, + tuple_len, location, ))?; - block_ok.append_op_result(arith::andi( - array_since_is_zero, - array_cap_matches, + let array_ptr = block_ok.extract_value( + context, location, - ))? - }; - - let box_ptr = block_ok.append_op_result(scf::r#if( - is_pointer_passthrough, - &[llvm::r#type::pointer(context, 0)], - { - let region = Region::new(); - let block = region.append_block(Block::new(&[])); - - // If can be passed through, just return the array ptr + entry.argument(0)?.into(), + llvm::r#type::pointer(context, 0), + 0, + )?; + let should_forward_pointer = + block_ok.append_op_result(arith::andi(starts_at_zero, capacity_matches, location))?; - block.append_operation(scf::r#yield(&[array_ptr], location)); + let block_clone = helper.append_block(Block::new(&[])); + let block_forward = helper.append_block(Block::new(&[])); + block_ok.append_operation(cf::cond_br( + context, + should_forward_pointer, + block_forward, + block_clone, + &[], + &[], + location, + )); - region - }, { - let region = Region::new(); - let block = region.append_block(Block::new(&[])); - - // Otherwise, alloc memory for the returned tuple and clone it - - let tuple_len_value = block.const_int(context, location, tuple_len, 64)?; - let elem_stride_value = block.const_int(context, location, elem_stride, 64)?; - let tuple_len_bytes = block.append_op_result(arith::muli( - tuple_len_value, - elem_stride_value, + let elem_stride = + block_clone.const_int(context, location, elem_layout.pad_to_align().size(), 64)?; + let tuple_len = block_clone.append_op_result(arith::extui( + tuple_len, + IntegerType::new(context, 64).into(), location, ))?; + let tuple_len = + block_clone.append_op_result(arith::muli(tuple_len, elem_stride, location))?; - let tuple_ptr = { - let null_ptr = block - .append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?; - let tuple_ptr = block.append_op_result(ReallocBindingsMeta::realloc( - context, - null_ptr, - tuple_len_bytes, - location, - ))?; - - assert_nonnull( - context, - block.deref(), - location, - tuple_ptr, - "realloc returned null", - )?; - - tuple_ptr - }; + let box_ptr = block_clone + .append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?; + let box_ptr = block_clone.append_op_result(ReallocBindingsMeta::realloc( + context, box_ptr, tuple_len, location, + ))?; - let array_start = block.append_op_result(arith::extui( + let elem_offset = block_clone.append_op_result(arith::extui( array_start, IntegerType::new(context, 64).into(), location, ))?; - let array_start_offset = - block.append_op_result(arith::muli(elem_stride_value, array_start, location))?; - - let src_ptr = block.append_op_result(llvm::get_element_ptr_dynamic( + let elem_offset = + block_clone.append_op_result(arith::muli(elem_offset, elem_stride, location))?; + let elem_ptr = block_clone.append_op_result(llvm::get_element_ptr_dynamic( context, array_ptr, - &[array_start_offset], + &[elem_offset], IntegerType::new(context, 8).into(), llvm::r#type::pointer(context, 0), location, ))?; - block.memcpy(context, location, src_ptr, tuple_ptr, tuple_len_bytes); - block.append_operation(scf::r#yield(&[tuple_ptr], location)); + block_clone.append_operation( + ods::llvm::intr_memcpy_inline( + context, + box_ptr, + elem_ptr, + IntegerAttribute::new( + IntegerType::new(context, 64).into(), + (tuple_len_val * elem_layout.pad_to_align().size()) as i64, + ), + IntegerAttribute::new(IntegerType::new(context, 1).into(), 0), + location, + ) + .into(), + ); - region - }, - location, - ))?; + block_clone.append_operation(ReallocBindingsMeta::free(context, array_ptr, location)); + block_clone.append_operation(helper.br(0, &[box_ptr], location)); + } + + block_forward.append_operation(helper.br(0, &[array_ptr], location)); + } - block_ok.append_operation(helper.br(0, &[box_ptr], location)); - block_err.append_operation(helper.br(1, &[], location)); + { + registry.build_type( + context, + helper, + registry, + metadata, + &info.signature.param_signatures[0].ty, + )?; + + // The following unwrap is unreachable because an array always has a drop implementation, + // which at this point is always inserted thanks to the `build_type()` just above. + metadata + .get::() + .unwrap() + .invoke_override( + context, + block_err, + location, + &info.signature.param_signatures[0].ty, + entry.argument(0)?.into(), + )?; + + block_err.append_operation(helper.br(1, &[], location)); + } Ok(()) } @@ -1751,18 +1934,18 @@ mod test { use box::BoxTrait; fn run_test() -> u32 { - let mut data: Array = ArrayTrait::new(); + let mut data: Array = ArrayTrait::new(); // Alloca (freed). data.append(1_u32); data.append(2_u32); data.append(3_u32); data.append(4_u32); - let sp = data.span(); + let sp = data.span(); // Alloca (leaked). let slice = sp.slice(1, 2); data.append(5_u32); data.append(5_u32); data.append(5_u32); data.append(5_u32); - data.append(5_u32); + data.append(5_u32); // Realloc (freed). data.append(5_u32); *slice.get(1).unwrap().unbox() } diff --git a/src/libfuncs/box.rs b/src/libfuncs/box.rs index f7840f772..9150162b5 100644 --- a/src/libfuncs/box.rs +++ b/src/libfuncs/box.rs @@ -12,10 +12,7 @@ use cairo_lang_sierra::{ extensions::{ boxing::BoxConcreteLibfunc, core::{CoreLibfunc, CoreType}, - lib_func::{ - BranchSignature, LibfuncSignature, SignatureAndTypeConcreteLibfunc, - SignatureOnlyConcreteLibfunc, - }, + lib_func::SignatureAndTypeConcreteLibfunc, }, program_registry::ProgramRegistry, }; @@ -152,37 +149,16 @@ pub fn build_unbox<'ctx, 'this>( } fn build_forward_snapshot<'ctx, 'this>( - context: &'ctx Context, - registry: &ProgramRegistry, + _context: &'ctx Context, + _registry: &ProgramRegistry, entry: &'this Block<'ctx>, location: Location<'ctx>, helper: &LibfuncHelper<'ctx, 'this>, - metadata: &mut MetadataStorage, - info: &SignatureAndTypeConcreteLibfunc, + _metadata: &mut MetadataStorage, + _info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { - super::snapshot_take::build( - context, - registry, - entry, - location, - helper, - metadata, - &SignatureOnlyConcreteLibfunc { - signature: LibfuncSignature { - param_signatures: info.signature.param_signatures.clone(), - branch_signatures: info - .signature - .branch_signatures - .iter() - .map(|x| BranchSignature { - vars: x.vars.clone(), - ap_change: x.ap_change.clone(), - }) - .collect(), - fallthrough: info.signature.fallthrough, - }, - }, - ) + entry.append_operation(helper.br(0, &[entry.argument(0)?.into()], location)); + Ok(()) } #[cfg(test)] diff --git a/src/libfuncs/debug.rs b/src/libfuncs/debug.rs index ed279aba2..a22ebb9ae 100644 --- a/src/libfuncs/debug.rs +++ b/src/libfuncs/debug.rs @@ -12,8 +12,10 @@ use super::LibfuncHelper; use crate::{ error::Result, - metadata::{runtime_bindings::RuntimeBindingsMeta, MetadataStorage}, - utils::BlockExt, + metadata::{ + drop_overrides::DropOverridesMeta, runtime_bindings::RuntimeBindingsMeta, MetadataStorage, + }, + utils::{BlockExt, ProgramRegistryExt}, }; use cairo_lang_sierra::{ extensions::{ @@ -47,12 +49,12 @@ pub fn build<'ctx>( pub fn build_print<'ctx>( context: &'ctx Context, - _registry: &ProgramRegistry, + registry: &ProgramRegistry, entry: &Block<'ctx>, location: Location<'ctx>, helper: &LibfuncHelper<'ctx, '_>, metadata: &mut MetadataStorage, - _info: &SignatureOnlyConcreteLibfunc, + info: &SignatureOnlyConcreteLibfunc, ) -> Result<()> { let stdout_fd = entry.const_int(context, location, 1, 32)?; @@ -105,6 +107,19 @@ pub fn build_print<'ctx>( context, helper, entry, stdout_fd, values_ptr, values_len, location, )?; + let input_ty = &info.signature.param_signatures[0].ty; + registry.build_type(context, helper, registry, metadata, input_ty)?; + metadata + .get::() + .unwrap() + .invoke_override( + context, + entry, + location, + input_ty, + entry.argument(0)?.into(), + )?; + let k0 = entry.const_int(context, location, 0, 32)?; let return_code_is_ok = entry.append_op_result(arith::cmpi( context, diff --git a/src/libfuncs/felt252.rs b/src/libfuncs/felt252.rs index 348ec476e..351650ce4 100644 --- a/src/libfuncs/felt252.rs +++ b/src/libfuncs/felt252.rs @@ -377,15 +377,8 @@ pub mod test { lazy_static! { static ref FELT252_ADD: (String, Program) = load_cairo! { - use core::debug::PrintTrait; fn run_test(lhs: felt252, rhs: felt252) -> felt252 { - lhs.print(); - rhs.print(); - let result = lhs + rhs; - - result.print(); - - result + lhs + rhs } }; diff --git a/src/libfuncs/felt252_dict.rs b/src/libfuncs/felt252_dict.rs index ce4977079..4fdc609b3 100644 --- a/src/libfuncs/felt252_dict.rs +++ b/src/libfuncs/felt252_dict.rs @@ -56,8 +56,7 @@ pub fn build_new<'ctx, 'this>( .get_mut::() .expect("Runtime library not available."); - let op = runtime_bindings.dict_new(context, helper, entry, location)?; - let dict_ptr = op.result(0)?.into(); + let dict_ptr = runtime_bindings.dict_new(context, helper, entry, location)?; entry.append_operation(helper.br(0, &[segment_arena, dict_ptr], location)); Ok(()) diff --git a/src/libfuncs/felt252_dict_entry.rs b/src/libfuncs/felt252_dict_entry.rs index 1fb38cf0f..bc09d6c05 100644 --- a/src/libfuncs/felt252_dict_entry.rs +++ b/src/libfuncs/felt252_dict_entry.rs @@ -4,11 +4,12 @@ use super::LibfuncHelper; use crate::{ error::Result, metadata::{ + drop_overrides::DropOverridesMeta, dup_overrides::DupOverridesMeta, realloc_bindings::ReallocBindingsMeta, runtime_bindings::RuntimeBindingsMeta, MetadataStorage, }, types::TypeBuilder, - utils::{get_integer_layout, BlockExt, ProgramRegistryExt}, + utils::{BlockExt, ProgramRegistryExt}, }; use cairo_lang_sierra::{ extensions::{ @@ -20,11 +21,8 @@ use cairo_lang_sierra::{ program_registry::ProgramRegistry, }; use melior::{ - dialect::{cf, llvm}, - ir::{ - attribute::IntegerAttribute, operation::OperationBuilder, r#type::IntegerType, Block, - Identifier, Location, Value, ValueLike, - }, + dialect::{cf, llvm, ods}, + ir::{attribute::IntegerAttribute, r#type::IntegerType, Block, Location}, Context, }; @@ -57,10 +55,6 @@ pub fn build_get<'ctx, 'this>( metadata: &mut MetadataStorage, info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { - if metadata.get::().is_none() { - metadata.insert(ReallocBindingsMeta::new(context, helper)); - } - let (key_ty, key_layout) = registry.build_type_with_layout( context, helper, @@ -68,7 +62,6 @@ pub fn build_get<'ctx, 'this>( metadata, &info.param_signatures()[1].ty, )?; - let entry_ty = registry.build_type( context, helper, @@ -76,169 +69,209 @@ pub fn build_get<'ctx, 'this>( metadata, &info.branch_signatures()[0].vars[0].ty, )?; - - let (value_ty, value_layout) = registry.build_type_with_layout( - context, - helper, - registry, - metadata, - &info.branch_signatures()[0].vars[1].ty, - )?; + let value_ty = registry.build_type(context, helper, registry, metadata, &info.ty)?; let dict_ptr = entry.argument(0)?.into(); - let key_value = entry.argument(1)?.into(); + let entry_key = entry.argument(1)?.into(); - let key_ptr = helper - .init_block() - .alloca1(context, location, key_ty, key_layout.align())?; + let entry_key_ptr = + helper + .init_block() + .alloca1(context, location, key_ty, key_layout.align())?; + entry.store(context, location, entry_key_ptr, entry_key)?; - entry.store(context, location, key_ptr, key_value)?; - - let runtime_bindings = metadata + // Double pointer. Avoid allocating an element on a dict getter. + let entry_value_ptr_ptr = metadata .get_mut::() - .expect("Runtime library not available."); - - let op = runtime_bindings.dict_get(context, helper, entry, dict_ptr, key_ptr, location)?; - let result_ptr: Value = op.result(0)?.into(); - - let null_ptr = entry.append_op_result( - OperationBuilder::new("llvm.mlir.zero", location) - .add_results(&[result_ptr.r#type()]) - .build()?, + .unwrap() + .dict_get(context, helper, entry, dict_ptr, entry_key_ptr, location)?; + let entry_value_ptr = entry.load( + context, + location, + entry_value_ptr_ptr, + llvm::r#type::pointer(context, 0), )?; - // need llvm instead of arith to compare pointers - let is_null_ptr = entry.append_op_result( - OperationBuilder::new("llvm.icmp", location) - .add_operands(&[result_ptr, null_ptr]) - .add_attributes(&[( - Identifier::new(context, "predicate"), - IntegerAttribute::new(IntegerType::new(context, 64).into(), 0).into(), - )]) - .add_results(&[IntegerType::new(context, 1).into()]) - .build()?, + let null_ptr = + entry.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?; + let is_vacant = entry.append_op_result( + ods::llvm::icmp( + context, + IntegerType::new(context, 1).into(), + entry_value_ptr, + null_ptr, + IntegerAttribute::new(IntegerType::new(context, 64).into(), 0).into(), + location, + ) + .into(), )?; - let block_is_null = helper.append_block(Block::new(&[])); - let block_is_found = helper.append_block(Block::new(&[])); - let block_final = helper.append_block(Block::new(&[ - (llvm::r#type::pointer(context, 0), location), - (value_ty, location), - ])); - + let block_occupied = helper.append_block(Block::new(&[])); + let block_vacant = helper.append_block(Block::new(&[])); + let block_final = helper.append_block(Block::new(&[(value_ty, location)])); entry.append_operation(cf::cond_br( context, - is_null_ptr, - block_is_null, - block_is_found, + is_vacant, + block_vacant, + block_occupied, &[], &[], location, )); - // null block { - let alloc_size = block_is_null.const_int(context, location, value_layout.size(), 64)?; + let value = block_occupied.load(context, location, entry_value_ptr, value_ty)?; + let values = match metadata.get::() { + Some(dup_overrides_meta) if dup_overrides_meta.is_overriden(&info.ty) => { + dup_overrides_meta.invoke_override( + context, + block_occupied, + location, + &info.ty, + value, + )? + } + _ => (value, value), + }; - let value_ptr = block_is_null.append_op_result(ReallocBindingsMeta::realloc( - context, result_ptr, alloc_size, location, - ))?; + block_occupied.store(context, location, entry_value_ptr, values.0)?; + block_occupied.append_operation(cf::br(block_final, &[values.1], location)); + } - let default_value = registry + { + let value = registry .get_type(&info.branch_signatures()[0].vars[1].ty)? .build_default( context, registry, - block_is_null, + block_vacant, location, helper, metadata, &info.branch_signatures()[0].vars[1].ty, )?; - - block_is_null.append_operation(cf::br(block_final, &[value_ptr, default_value], location)); + block_vacant.append_operation(cf::br(block_final, &[value], location)); } - // found block - { - let loaded_val_ptr = block_is_found.load(context, location, result_ptr, value_ty)?; - block_is_found.append_operation(cf::br( - block_final, - &[result_ptr, loaded_val_ptr], - location, - )); - } - - // construct the struct - - let entry_value = block_final.append_op_result(llvm::undef(entry_ty, location))?; - - let value_ptr = block_final.argument(0)?.into(); - let value = block_final.argument(1)?.into(); - - let entry_value = block_final.insert_value(context, location, entry_value, key_value, 0)?; - - let entry_value = block_final.insert_value(context, location, entry_value, value_ptr, 1)?; - - let entry_value = block_final.insert_value(context, location, entry_value, dict_ptr, 2)?; - - block_final.append_operation(helper.br(0, &[entry_value, value], location)); + let entry = block_final.append_op_result(llvm::undef(entry_ty, location))?; + let entry = + block_final.insert_values(context, location, entry, &[dict_ptr, entry_value_ptr_ptr])?; + block_final.append_operation(helper.br(0, &[entry, block_final.argument(0)?.into()], location)); Ok(()) } pub fn build_finalize<'ctx, 'this>( context: &'ctx Context, - _registry: &ProgramRegistry, + registry: &ProgramRegistry, entry: &'this Block<'ctx>, location: Location<'ctx>, helper: &LibfuncHelper<'ctx, 'this>, metadata: &mut MetadataStorage, - _info: &SignatureAndTypeConcreteLibfunc, + info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { - let key_ty = IntegerType::new(context, 252).into(); - let key_layout = get_integer_layout(252); + if metadata.get::().is_none() { + metadata.insert(ReallocBindingsMeta::new(context, helper)); + } - let entry_value = entry.argument(0)?.into(); - let new_value = entry.argument(1)?.into(); + let (value_ty, value_layout) = registry.build_type_with_layout( + context, + helper, + registry, + metadata, + &info.signature.param_signatures[1].ty, + )?; - let key_value = entry.extract_value(context, location, entry_value, key_ty, 0)?; + let dict_entry = entry.argument(0)?.into(); + let entry_value = entry.argument(1)?.into(); - let value_ptr = entry.extract_value( + let dict_ptr = entry.extract_value( context, location, - entry_value, + dict_entry, + llvm::r#type::pointer(context, 0), + 0, + )?; + let value_ptr_ptr = entry.extract_value( + context, + location, + dict_entry, llvm::r#type::pointer(context, 0), 1, )?; - let dict_ptr = entry.extract_value( + let value_ptr = entry.load( context, location, - entry_value, + value_ptr_ptr, llvm::r#type::pointer(context, 0), - 2, )?; - entry.store(context, location, value_ptr, new_value)?; + let null_ptr = + entry.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?; + let is_vacant = entry.append_op_result( + ods::llvm::icmp( + context, + IntegerType::new(context, 1).into(), + value_ptr, + null_ptr, + IntegerAttribute::new(IntegerType::new(context, 64).into(), 0).into(), + location, + ) + .into(), + )?; + + let block_occupied = helper.append_block(Block::new(&[])); + let block_vacant = helper.append_block(Block::new(&[])); + let block_final = + helper.append_block(Block::new(&[(llvm::r#type::pointer(context, 0), location)])); + entry.append_operation(cf::cond_br( + context, + is_vacant, + block_vacant, + block_occupied, + &[], + &[], + location, + )); - let key_ptr = helper - .init_block() - .alloca1(context, location, key_ty, key_layout.align())?; + { + match metadata.get::() { + Some(drop_overrides_meta) + if drop_overrides_meta.is_overriden(&info.signature.param_signatures[1].ty) => + { + let value = block_occupied.load(context, location, value_ptr, value_ty)?; + drop_overrides_meta.invoke_override( + context, + block_occupied, + location, + &info.signature.param_signatures[1].ty, + value, + )?; + } + _ => {} + } - entry.store(context, location, key_ptr, key_value)?; + block_occupied.append_operation(cf::br(block_final, &[value_ptr], location)); + } - // call insert + { + let value_len = block_vacant.const_int(context, location, value_layout.size(), 64)?; + let value_ptr = block_vacant.append_op_result(ReallocBindingsMeta::realloc( + context, null_ptr, value_len, location, + ))?; - let runtime_bindings = metadata - .get_mut::() - .expect("Runtime library not available."); + block_vacant.store(context, location, value_ptr_ptr, value_ptr)?; + block_vacant.append_operation(cf::br(block_final, &[value_ptr], location)); + } - runtime_bindings.dict_insert( - context, helper, entry, dict_ptr, key_ptr, value_ptr, location, + block_final.store( + context, + location, + block_final.argument(0)?.into(), + entry_value, )?; - - entry.append_operation(helper.br(0, &[dict_ptr], location)); + block_final.append_operation(helper.br(0, &[dict_ptr], location)); Ok(()) } diff --git a/src/libfuncs/function_call.rs b/src/libfuncs/function_call.rs index 4594219ad..b3b80d4a3 100644 --- a/src/libfuncs/function_call.rs +++ b/src/libfuncs/function_call.rs @@ -23,7 +23,7 @@ use melior::{ attribute::{DenseI32ArrayAttribute, FlatSymbolRefAttribute}, operation::OperationBuilder, r#type::IntegerType, - Block, Identifier, Location, Type, Value, + Attribute, Block, Identifier, Location, Type, Value, }, Context, }; @@ -195,10 +195,10 @@ pub fn build<'ctx, 'this>( ) .into(), ), - // ( - // Identifier::new(context, "CConv"), - // Attribute::parse(context, "#llvm.cconv").unwrap(), - // ), + ( + Identifier::new(context, "CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), ]) .add_operands(&arguments) .add_results(&[llvm::r#type::r#struct(context, &result_types, false)]) diff --git a/src/libfuncs/gas.rs b/src/libfuncs/gas.rs index 474fe7443..b7eabecc9 100644 --- a/src/libfuncs/gas.rs +++ b/src/libfuncs/gas.rs @@ -9,7 +9,7 @@ use crate::{ use cairo_lang_sierra::{ extensions::{ core::{CoreLibfunc, CoreType}, - gas::GasConcreteLibfunc, + gas::{CostTokenType, GasConcreteLibfunc}, lib_func::SignatureOnlyConcreteLibfunc, ConcreteLibfunc, }, @@ -18,9 +18,10 @@ use cairo_lang_sierra::{ use melior::{ dialect::{ arith::{self, CmpiPredicate}, - llvm, ods, + llvm::{self, r#type::pointer}, + ods, }, - ir::{r#type::IntegerType, Block, Location}, + ir::{attribute::FlatSymbolRefAttribute, r#type::IntegerType, Block, Location}, Context, }; @@ -83,22 +84,74 @@ pub fn build_withdraw_gas<'ctx, 'this>( super::increment_builtin_counter(context, entry, location, entry.argument(0)?.into())?; let current_gas = entry.argument(1)?.into(); - let cost = metadata.get::().and_then(|x| x.0); + let gas_cost = metadata + .get::() + .expect("builtin_withdraw_gas should always have a gas cost"); let u128_type: melior::ir::Type = IntegerType::new(context, 128).into(); - let gas_cost_val = - entry.const_int_from_type(context, location, cost.unwrap_or(0), u128_type)?; + let u64_type: melior::ir::Type = IntegerType::new(context, 64).into(); + + // Get the ptr to the global, holding a ptr to the list. + let builtin_ptr_ptr = entry.append_op_result( + ods::llvm::mlir_addressof( + context, + pointer(context, 0), + FlatSymbolRefAttribute::new(context, "builtin_costs"), + location, + ) + .into(), + )?; + + let builtin_ptr = entry.load(context, location, builtin_ptr_ptr, pointer(context, 0))?; + + let mut final_gas_cost = entry.const_int_from_type(context, location, 0, u128_type)?; + + for (cost, token_type) in &gas_cost.0 { + if *cost == 0 { + continue; + } + + let token_type_index = match token_type { + CostTokenType::Const => 0, + CostTokenType::Pedersen => 1, + CostTokenType::Bitwise => 2, + CostTokenType::EcOp => 3, + CostTokenType::Poseidon => 4, + CostTokenType::AddMod => 5, + CostTokenType::MulMod => 6, + _ => unreachable!(), + }; + + let gas_cost_val = entry.const_int_from_type(context, location, *cost, u128_type)?; + let token_type_index_val = + entry.const_int_from_type(context, location, token_type_index, u64_type)?; + + let cost_value_ptr = entry.append_op_result(llvm::get_element_ptr_dynamic( + context, + builtin_ptr, + &[token_type_index_val], + u64_type, + pointer(context, 0), + location, + ))?; + let cost_value = entry.load(context, location, cost_value_ptr, u64_type)?; + let cost_value = entry.append_op_result(arith::extui(cost_value, u128_type, location))?; + let total_gas_cost_val = + entry.append_op_result(arith::muli(gas_cost_val, cost_value, location))?; + final_gas_cost = + entry.append_op_result(arith::addi(final_gas_cost, total_gas_cost_val, location))?; + } let is_enough = entry.append_op_result(arith::cmpi( context, CmpiPredicate::Uge, current_gas, - gas_cost_val, + final_gas_cost, location, ))?; let resulting_gas = entry.append_op_result( - ods::llvm::intr_usub_sat(context, current_gas, gas_cost_val, location).into(), + ods::llvm::intr_usub_sat(context, current_gas, final_gas_cost, location).into(), )?; entry.append_operation(helper.cond_br( @@ -125,23 +178,63 @@ pub fn build_builtin_withdraw_gas<'ctx, 'this>( let range_check = super::increment_builtin_counter(context, entry, location, entry.argument(0)?.into())?; let current_gas = entry.argument(1)?.into(); + let builtin_ptr = entry.argument(2)?.into(); - let cost = metadata.get::().and_then(|x| x.0); + let gas_cost = metadata + .get::() + .expect("builtin_withdraw_gas should always have a gas cost"); let u128_type: melior::ir::Type = IntegerType::new(context, 128).into(); - let gas_cost_val = - entry.const_int_from_type(context, location, cost.unwrap_or(0), u128_type)?; + let u64_type: melior::ir::Type = IntegerType::new(context, 64).into(); + + let mut final_gas_cost = entry.const_int_from_type(context, location, 0, u128_type)?; + + for (cost, token_type) in &gas_cost.0 { + if *cost == 0 { + continue; + } + + let token_type_index = match token_type { + CostTokenType::Const => 0, + CostTokenType::Pedersen => 1, + CostTokenType::Bitwise => 2, + CostTokenType::EcOp => 3, + CostTokenType::Poseidon => 4, + CostTokenType::AddMod => 5, + CostTokenType::MulMod => 6, + _ => unreachable!(), + }; + + let gas_cost_val = entry.const_int_from_type(context, location, *cost, u128_type)?; + let token_type_index_val = + entry.const_int_from_type(context, location, token_type_index, u64_type)?; + + let cost_value_ptr = entry.append_op_result(llvm::get_element_ptr_dynamic( + context, + builtin_ptr, + &[token_type_index_val], + u64_type, + pointer(context, 0), + location, + ))?; + let cost_value = entry.load(context, location, cost_value_ptr, u64_type)?; + let cost_value = entry.append_op_result(arith::extui(cost_value, u128_type, location))?; + let total_gas_cost_val = + entry.append_op_result(arith::muli(gas_cost_val, cost_value, location))?; + final_gas_cost = + entry.append_op_result(arith::addi(final_gas_cost, total_gas_cost_val, location))?; + } let is_enough = entry.append_op_result(arith::cmpi( context, CmpiPredicate::Uge, current_gas, - gas_cost_val, + final_gas_cost, location, ))?; let resulting_gas = entry.append_op_result( - ods::llvm::intr_usub_sat(context, current_gas, gas_cost_val, location).into(), + ods::llvm::intr_usub_sat(context, current_gas, final_gas_cost, location).into(), )?; entry.append_operation(helper.cond_br( @@ -173,10 +266,20 @@ pub fn build_get_builtin_costs<'ctx, 'this>( &info.branch_signatures()[0].vars[0].ty, )?; - // TODO: Implement libfunc. - let op0 = entry.append_op_result(llvm::undef(builtin_costs_ty, location))?; + // Get the ptr to the global, holding a ptr to the list. + let builtin_ptr_ptr = entry.append_op_result( + ods::llvm::mlir_addressof( + context, + pointer(context, 0), + FlatSymbolRefAttribute::new(context, "builtin_costs"), + location, + ) + .into(), + )?; + + let builtin_ptr = entry.load(context, location, builtin_ptr_ptr, builtin_costs_ty)?; - entry.append_operation(helper.br(0, &[op0], location)); + entry.append_operation(helper.br(0, &[builtin_ptr], location)); Ok(()) } diff --git a/src/libfuncs/nullable.rs b/src/libfuncs/nullable.rs index b1fed17fc..974dcef29 100644 --- a/src/libfuncs/nullable.rs +++ b/src/libfuncs/nullable.rs @@ -7,10 +7,7 @@ use crate::{error::Result, metadata::MetadataStorage, utils::BlockExt}; use cairo_lang_sierra::{ extensions::{ core::{CoreLibfunc, CoreType}, - lib_func::{ - BranchSignature, LibfuncSignature, SignatureAndTypeConcreteLibfunc, - SignatureOnlyConcreteLibfunc, - }, + lib_func::{SignatureAndTypeConcreteLibfunc, SignatureOnlyConcreteLibfunc}, nullable::NullableConcreteLibfunc, }, program_registry::ProgramRegistry, @@ -132,37 +129,16 @@ fn build_match_nullable<'ctx, 'this>( } fn build_forward_snapshot<'ctx, 'this>( - context: &'ctx Context, - registry: &ProgramRegistry, + _context: &'ctx Context, + _registry: &ProgramRegistry, entry: &'this Block<'ctx>, location: Location<'ctx>, helper: &LibfuncHelper<'ctx, 'this>, - metadata: &mut MetadataStorage, - info: &SignatureAndTypeConcreteLibfunc, + _metadata: &mut MetadataStorage, + _info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { - super::snapshot_take::build( - context, - registry, - entry, - location, - helper, - metadata, - &SignatureOnlyConcreteLibfunc { - signature: LibfuncSignature { - param_signatures: info.signature.param_signatures.clone(), - branch_signatures: info - .signature - .branch_signatures - .iter() - .map(|x| BranchSignature { - vars: x.vars.clone(), - ap_change: x.ap_change.clone(), - }) - .collect(), - fallthrough: info.signature.fallthrough, - }, - }, - ) + entry.append_operation(helper.br(0, &[entry.argument(0)?.into()], location)); + Ok(()) } #[cfg(test)] diff --git a/src/libfuncs/sint128.rs b/src/libfuncs/sint128.rs index ccee4c372..4ce06cbf8 100644 --- a/src/libfuncs/sint128.rs +++ b/src/libfuncs/sint128.rs @@ -22,6 +22,7 @@ use melior::{ dialect::{ arith::{self, CmpiPredicate}, cf, llvm, + ods::math, }, ir::{operation::OperationBuilder, r#type::IntegerType, Block, Location, Value, ValueLike}, Context, @@ -244,11 +245,33 @@ pub fn build_to_felt252<'ctx, 'this>( metadata, &info.branch_signatures()[0].vars[0].ty, )?; + let prime = entry.const_int_from_type(context, location, PRIME.clone(), felt252_ty)?; + let value: Value = entry.argument(0)?.into(); + let value_type = value.r#type(); + + let is_negative = entry.append_op_result(arith::cmpi( + context, + arith::CmpiPredicate::Slt, + value, + entry.const_int_from_type(context, location, 0, value_type)?, + location, + ))?; + + let value_abs = entry.append_op_result(math::absi(context, value, location).into())?; - let result = entry.append_op_result(arith::extui(value, felt252_ty, location))?; + let result = entry.append_op_result(arith::extui(value_abs, felt252_ty, location))?; - entry.append_operation(helper.br(0, &[result], location)); + let prime_minus_result = entry.append_op_result(arith::subi(prime, result, location))?; + + let final_result = entry.append_op_result(arith::select( + is_negative, + prime_minus_result, + result, + location, + ))?; + + entry.append_operation(helper.br(0, &[final_result], location)); Ok(()) } @@ -468,7 +491,7 @@ mod test { } #[test] - fn i128_to_felt252() { + fn pos_i128_to_felt252() { let program = load_cairo!( use traits::Into; @@ -476,8 +499,24 @@ mod test { 2_i128.into() } ); + run_program_assert_output(&program, "run_test", &[], Felt::from(2_i128).into()); + } - run_program_assert_output(&program, "run_test", &[], Felt::from(2).into()); + #[test] + fn neg_i128_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + -396372399979_i128.into() + } + ); + run_program_assert_output( + &program, + "run_test", + &[], + Felt::from(-396372399979_i128).into(), + ); } #[test] diff --git a/src/libfuncs/sint16.rs b/src/libfuncs/sint16.rs index 2d3821cf2..5211e0246 100644 --- a/src/libfuncs/sint16.rs +++ b/src/libfuncs/sint16.rs @@ -22,6 +22,7 @@ use melior::{ dialect::{ arith::{self, CmpiPredicate}, cf, llvm, + ods::math, }, ir::{operation::OperationBuilder, r#type::IntegerType, Block, Location, Value, ValueLike}, Context, @@ -274,11 +275,33 @@ pub fn build_to_felt252<'ctx, 'this>( metadata, &info.branch_signatures()[0].vars[0].ty, )?; + let prime = entry.const_int_from_type(context, location, PRIME.clone(), felt252_ty)?; + let value: Value = entry.argument(0)?.into(); + let value_type = value.r#type(); - let result = entry.append_op_result(arith::extui(value, felt252_ty, location))?; + let is_negative = entry.append_op_result(arith::cmpi( + context, + arith::CmpiPredicate::Slt, + value, + entry.const_int_from_type(context, location, 0, value_type)?, + location, + ))?; - entry.append_operation(helper.br(0, &[result], location)); + let value_abs = entry.append_op_result(math::absi(context, value, location).into())?; + + let result = entry.append_op_result(arith::extui(value_abs, felt252_ty, location))?; + + let prime_minus_result = entry.append_op_result(arith::subi(prime, result, location))?; + + let final_result = entry.append_op_result(arith::select( + is_negative, + prime_minus_result, + result, + location, + ))?; + + entry.append_operation(helper.br(0, &[final_result], location)); Ok(()) } @@ -504,7 +527,7 @@ mod test { } #[test] - fn i16_to_felt252() { + fn pos_i16_to_felt252() { let program = load_cairo!( use traits::Into; @@ -516,6 +539,18 @@ mod test { run_program_assert_output(&program, "run_test", &[], Felt::from(2).into()); } + #[test] + fn neg_i16_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + -3963_i16.into() + } + ); + run_program_assert_output(&program, "run_test", &[], Felt::from(-3963).into()); + } + #[test] fn i16_from_felt252() { let program = load_cairo!( diff --git a/src/libfuncs/sint32.rs b/src/libfuncs/sint32.rs index 0de6c839f..3e8bd9139 100644 --- a/src/libfuncs/sint32.rs +++ b/src/libfuncs/sint32.rs @@ -22,6 +22,7 @@ use melior::{ dialect::{ arith::{self, CmpiPredicate}, cf, llvm, + ods::math, }, ir::{operation::OperationBuilder, r#type::IntegerType, Block, Location, Value, ValueLike}, Context, @@ -273,11 +274,33 @@ pub fn build_to_felt252<'ctx, 'this>( metadata, &info.branch_signatures()[0].vars[0].ty, )?; + let prime = entry.const_int_from_type(context, location, PRIME.clone(), felt252_ty)?; + let value: Value = entry.argument(0)?.into(); + let value_type = value.r#type(); - let result = entry.append_op_result(arith::extui(value, felt252_ty, location))?; + let is_negative = entry.append_op_result(arith::cmpi( + context, + arith::CmpiPredicate::Slt, + value, + entry.const_int_from_type(context, location, 0, value_type)?, + location, + ))?; - entry.append_operation(helper.br(0, &[result], location)); + let value_abs = entry.append_op_result(math::absi(context, value, location).into())?; + + let result = entry.append_op_result(arith::extui(value_abs, felt252_ty, location))?; + + let prime_minus_result = entry.append_op_result(arith::subi(prime, result, location))?; + + let final_result = entry.append_op_result(arith::select( + is_negative, + prime_minus_result, + result, + location, + ))?; + + entry.append_operation(helper.br(0, &[final_result], location)); Ok(()) } @@ -504,7 +527,7 @@ mod test { } #[test] - fn i32_to_felt252() { + fn pos_i32_to_felt252() { let program = load_cairo!( use traits::Into; @@ -516,6 +539,18 @@ mod test { run_program_assert_output(&program, "run_test", &[], Felt::from(2).into()); } + #[test] + fn neg_i32_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + -396372_i32.into() + } + ); + run_program_assert_output(&program, "run_test", &[], Felt::from(-396372).into()); + } + #[test] fn i32_from_felt252() { let program = load_cairo!( diff --git a/src/libfuncs/sint64.rs b/src/libfuncs/sint64.rs index 745935da5..a7ec6bc6c 100644 --- a/src/libfuncs/sint64.rs +++ b/src/libfuncs/sint64.rs @@ -22,6 +22,7 @@ use melior::{ dialect::{ arith::{self, CmpiPredicate}, cf, llvm, + ods::math, }, ir::{operation::OperationBuilder, r#type::IntegerType, Block, Location, Value, ValueLike}, Context, @@ -274,11 +275,33 @@ pub fn build_to_felt252<'ctx, 'this>( metadata, &info.branch_signatures()[0].vars[0].ty, )?; + let prime = entry.const_int_from_type(context, location, PRIME.clone(), felt252_ty)?; + let value: Value = entry.argument(0)?.into(); + let value_type = value.r#type(); - let result = entry.append_op_result(arith::extui(value, felt252_ty, location))?; + let is_negative = entry.append_op_result(arith::cmpi( + context, + arith::CmpiPredicate::Slt, + value, + entry.const_int_from_type(context, location, 0, value_type)?, + location, + ))?; - entry.append_operation(helper.br(0, &[result], location)); + let value_abs = entry.append_op_result(math::absi(context, value, location).into())?; + + let result = entry.append_op_result(arith::extui(value_abs, felt252_ty, location))?; + + let prime_minus_result = entry.append_op_result(arith::subi(prime, result, location))?; + + let final_result = entry.append_op_result(arith::select( + is_negative, + prime_minus_result, + result, + location, + ))?; + + entry.append_operation(helper.br(0, &[final_result], location)); Ok(()) } @@ -504,7 +527,7 @@ mod test { } #[test] - fn i64_to_felt252() { + fn pos_i64_to_felt252() { let program = load_cairo!( use traits::Into; @@ -516,6 +539,18 @@ mod test { run_program_assert_output(&program, "run_test", &[], Felt::from(2).into()); } + #[test] + fn neg_i64_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + -396372_i64.into() + } + ); + run_program_assert_output(&program, "run_test", &[], Felt::from(-396372).into()); + } + #[test] fn i64_from_felt252() { let program = load_cairo!( diff --git a/src/libfuncs/sint8.rs b/src/libfuncs/sint8.rs index f87b30520..99c3705bc 100644 --- a/src/libfuncs/sint8.rs +++ b/src/libfuncs/sint8.rs @@ -22,6 +22,7 @@ use melior::{ dialect::{ arith::{self, CmpiPredicate}, cf, llvm, + ods::math, }, ir::{operation::OperationBuilder, r#type::IntegerType, Block, Location, Value, ValueLike}, Context, @@ -275,11 +276,33 @@ pub fn build_to_felt252<'ctx, 'this>( metadata, &info.branch_signatures()[0].vars[0].ty, )?; + let prime = entry.const_int_from_type(context, location, PRIME.clone(), felt252_ty)?; + let value: Value = entry.argument(0)?.into(); + let value_type = value.r#type(); - let result = entry.append_op_result(arith::extui(value, felt252_ty, location))?; + let is_negative = entry.append_op_result(arith::cmpi( + context, + arith::CmpiPredicate::Slt, + value, + entry.const_int_from_type(context, location, 0, value_type)?, + location, + ))?; - entry.append_operation(helper.br(0, &[result], location)); + let value_abs = entry.append_op_result(math::absi(context, value, location).into())?; + + let result = entry.append_op_result(arith::extui(value_abs, felt252_ty, location))?; + + let prime_minus_result = entry.append_op_result(arith::subi(prime, result, location))?; + + let final_result = entry.append_op_result(arith::select( + is_negative, + prime_minus_result, + result, + location, + ))?; + + entry.append_operation(helper.br(0, &[final_result], location)); Ok(()) } @@ -506,7 +529,7 @@ mod test { } #[test] - fn i8_to_felt252() { + fn pos_i8_to_felt252() { let program = load_cairo!( use traits::Into; @@ -518,6 +541,18 @@ mod test { run_program_assert_output(&program, "run_test", &[], Felt::from(2).into()); } + #[test] + fn neg_i8_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + -255_i128.into() + } + ); + run_program_assert_output(&program, "run_test", &[], Felt::from(-255).into()); + } + #[test] fn i8_from_felt252() { let program = load_cairo!( diff --git a/src/libfuncs/starknet.rs b/src/libfuncs/starknet.rs index 7beeeafc6..c4ca0552d 100644 --- a/src/libfuncs/starknet.rs +++ b/src/libfuncs/starknet.rs @@ -4,7 +4,7 @@ use super::LibfuncHelper; use crate::{ error::Result, ffi::get_struct_field_type_at, - metadata::MetadataStorage, + metadata::{drop_overrides::DropOverridesMeta, MetadataStorage}, starknet::handler::StarknetSyscallHandlerCallbacks, utils::{get_integer_layout, BlockExt, ProgramRegistryExt, PRIME}, }; @@ -24,9 +24,7 @@ use melior::{ llvm::{self, LoadStoreOptions}, }, ir::{ - attribute::{ - DenseI32ArrayAttribute, DenseI64ArrayAttribute, IntegerAttribute, TypeAttribute, - }, + attribute::{DenseI32ArrayAttribute, DenseI64ArrayAttribute, TypeAttribute}, operation::OperationBuilder, r#type::IntegerType, Attribute, Block, Identifier, Location, Type, ValueLike, @@ -2981,7 +2979,6 @@ pub fn build_sha256_process_block_syscall<'ctx, 'this>( metadata: &mut MetadataStorage, info: &SignatureOnlyConcreteLibfunc, ) -> Result<()> { - // todo: do // Extract self pointer. let ptr = entry .append_operation(llvm::load( @@ -2995,7 +2992,7 @@ pub fn build_sha256_process_block_syscall<'ctx, 'this>( .into(); // Allocate space for the return value. - let (result_layout, (result_tag_ty, result_tag_layout), variant_tys) = + let (result_layout, (result_tag_ty, _), variant_tys) = crate::types::r#enum::get_type_for_variants( context, helper, @@ -3007,50 +3004,22 @@ pub fn build_sha256_process_block_syscall<'ctx, 'this>( ], )?; - let k1 = helper - .init_block() - .append_operation(arith::constant( + let result_ptr = helper.init_block().alloca1( + context, + location, + llvm::r#type::r#struct( context, - IntegerAttribute::new(IntegerType::new(context, 64).into(), 1).into(), - location, - )) - .result(0)? - .into(); - let result_ptr = helper - .init_block() - .append_operation( - OperationBuilder::new("llvm.alloca", location) - .add_attributes(&[ - ( - Identifier::new(context, "alignment"), - IntegerAttribute::new( - IntegerType::new(context, 64).into(), - result_layout.align().try_into()?, - ) - .into(), - ), - ( - Identifier::new(context, "elem_type"), - TypeAttribute::new(llvm::r#type::r#struct( - context, - &[ - result_tag_ty, - llvm::r#type::array( - IntegerType::new(context, 8).into(), - (result_layout.size() - 1).try_into()?, - ), - ], - false, - )) - .into(), - ), - ]) - .add_operands(&[k1]) - .add_results(&[llvm::r#type::pointer(context, 0)]) - .build()?, - ) - .result(0)? - .into(); + &[ + result_tag_ty, + llvm::r#type::array( + IntegerType::new(context, 8).into(), + (result_layout.size() - 1).try_into()?, + ), + ], + false, + ), + result_layout.align(), + )?; // Allocate space and write the current gas. let gas_builtin_ptr = helper.init_block().alloca1( @@ -3108,118 +3077,51 @@ pub fn build_sha256_process_block_syscall<'ctx, 'this>( .build()?, ); - let result = entry - .append_operation(llvm::load( - context, - result_ptr, - llvm::r#type::r#struct( - context, - &[ - result_tag_ty, - llvm::r#type::array( - IntegerType::new(context, 8).into(), - (result_layout.size() - 1).try_into()?, - ), - ], - false, - ), - location, - LoadStoreOptions::default(), - )) - .result(0)? - .into(); - let result_tag = entry - .append_operation(llvm::extract_value( + registry.build_type( + context, + helper, + registry, + metadata, + &info.signature.param_signatures[3].ty, + )?; + metadata + .get::() + .unwrap() + .invoke_override( context, - result, - DenseI64ArrayAttribute::new(context, &[0]), - IntegerType::new(context, 1).into(), + entry, location, - )) - .result(0)? - .into(); + &info.signature.param_signatures[3].ty, + sha256_current_block_ptr, + )?; + + let result_tag = entry.load(context, location, result_ptr, result_tag_ty)?; let payload_ok = { - let ptr = entry - .append_operation( - OperationBuilder::new("llvm.getelementptr", location) - .add_attributes(&[ - ( - Identifier::new(context, "rawConstantIndices"), - DenseI32ArrayAttribute::new( - context, - &[result_tag_layout.extend(variant_tys[0].1)?.1.try_into()?], - ) - .into(), - ), - ( - Identifier::new(context, "elem_type"), - TypeAttribute::new(IntegerType::new(context, 8).into()).into(), - ), - ]) - .add_operands(&[result_ptr]) - .add_results(&[llvm::r#type::pointer(context, 0)]) - .build()?, - ) - .result(0)? - .into(); - entry - .append_operation(llvm::load( - context, - ptr, - variant_tys[0].0, - location, - LoadStoreOptions::default(), - )) - .result(0)? - .into() + let value = entry.load( + context, + location, + result_ptr, + llvm::r#type::r#struct(context, &[result_tag_ty, variant_tys[0].0], false), + )?; + entry.extract_value(context, location, value, variant_tys[0].0, 1)? }; let payload_err = { - let ptr = entry - .append_operation( - OperationBuilder::new("llvm.getelementptr", location) - .add_attributes(&[ - ( - Identifier::new(context, "rawConstantIndices"), - DenseI32ArrayAttribute::new( - context, - &[result_tag_layout.extend(variant_tys[1].1)?.1.try_into()?], - ) - .into(), - ), - ( - Identifier::new(context, "elem_type"), - TypeAttribute::new(IntegerType::new(context, 8).into()).into(), - ), - ]) - .add_operands(&[result_ptr]) - .add_results(&[llvm::r#type::pointer(context, 0)]) - .build()?, - ) - .result(0)? - .into(); - entry - .append_operation(llvm::load( - context, - ptr, - variant_tys[1].0, - location, - LoadStoreOptions::default(), - )) - .result(0)? - .into() - }; - - let remaining_gas = entry - .append_operation(llvm::load( + let value = entry.load( context, - gas_builtin_ptr, - IntegerType::new(context, 128).into(), location, - LoadStoreOptions::default(), - )) - .result(0)? - .into(); + result_ptr, + llvm::r#type::r#struct(context, &[result_tag_ty, variant_tys[1].0], false), + )?; + entry.extract_value(context, location, value, variant_tys[1].0, 1)? + }; + + let remaining_gas = entry.load( + context, + location, + gas_builtin_ptr, + IntegerType::new(context, 128).into(), + )?; entry.append_operation(helper.cond_br( context, diff --git a/src/metadata/drop_overrides.rs b/src/metadata/drop_overrides.rs index 85d21ab1d..e0a1b30b8 100644 --- a/src/metadata/drop_overrides.rs +++ b/src/metadata/drop_overrides.rs @@ -32,7 +32,7 @@ use melior::{ ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, r#type::FunctionType, - Block, Location, Module, Region, Value, + Attribute, Block, Identifier, Location, Module, Region, Value, }, Context, }; @@ -85,7 +85,20 @@ impl DropOverridesMeta { StringAttribute::new(context, &format!("drop${}", id.id)), TypeAttribute::new(FunctionType::new(context, &[ty], &[]).into()), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "llvm.CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } diff --git a/src/metadata/dup_overrides.rs b/src/metadata/dup_overrides.rs index 164330963..dada18dd5 100644 --- a/src/metadata/dup_overrides.rs +++ b/src/metadata/dup_overrides.rs @@ -32,7 +32,7 @@ use melior::{ ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, r#type::FunctionType, - Block, Location, Module, Region, Value, ValueLike, + Attribute, Block, Identifier, Location, Module, Region, Value, ValueLike, }, Context, }; @@ -85,7 +85,20 @@ impl DupOverridesMeta { StringAttribute::new(context, &format!("dup${}", id.id)), TypeAttribute::new(FunctionType::new(context, &[ty], &[ty, ty]).into()), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "llvm.CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } diff --git a/src/metadata/gas.rs b/src/metadata/gas.rs index 9378c5895..9bb2ac34b 100644 --- a/src/metadata/gas.rs +++ b/src/metadata/gas.rs @@ -20,8 +20,9 @@ pub struct GasMetadata { pub gas_info: GasInfo, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct GasCost(pub Option); +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +// Cost, token type (index into builtin costs). +pub struct GasCost(pub Vec<(u128, CostTokenType)>); /// Configuration for metadata computation. #[derive(Debug, Clone)] @@ -102,16 +103,18 @@ impl GasMetadata { ) } - pub fn get_gas_cost_for_statement(&self, idx: StatementIdx) -> Option { - let mut cost = None; + pub fn get_gas_costs_for_statement(&self, idx: StatementIdx) -> Vec<(u128, CostTokenType)> { + let mut costs = Vec::new(); for cost_type in CostTokenType::iter_casm_tokens() { if let Some(amount) = self.get_gas_cost_for_statement_and_cost_token_type(idx, *cost_type) { - *cost.get_or_insert(0) += amount * token_gas_cost(*cost_type) as u128; + if amount > 0 { + costs.push((amount, *cost_type)); + } } } - cost + costs } pub fn get_gas_cost_for_statement_and_cost_token_type( diff --git a/src/metadata/realloc_bindings.rs b/src/metadata/realloc_bindings.rs index 1bd5793f3..369bc709f 100644 --- a/src/metadata/realloc_bindings.rs +++ b/src/metadata/realloc_bindings.rs @@ -4,39 +4,34 @@ //! compilation context. use melior::{ - dialect::{func, llvm}, + dialect::llvm, ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, - r#type::{FunctionType, IntegerType}, + operation::OperationBuilder, + r#type::IntegerType, Identifier, Location, Module, Operation, Region, Value, }, Context, }; -use std::marker::PhantomData; /// Memory allocation `realloc` metadata. #[derive(Debug)] -pub struct ReallocBindingsMeta { - phantom: PhantomData<()>, -} +pub struct ReallocBindingsMeta; impl ReallocBindingsMeta { /// Register the bindings to the `realloc` C function and return the metadata. pub fn new(context: &Context, module: &Module) -> Self { - module.body().append_operation(func::func( + module.body().append_operation(llvm::func( context, StringAttribute::new(context, "realloc"), - TypeAttribute::new( - FunctionType::new( - context, - &[ - llvm::r#type::pointer(context, 0), - IntegerType::new(context, 64).into(), - ], - &[llvm::r#type::pointer(context, 0)], - ) - .into(), - ), + TypeAttribute::new(llvm::r#type::function( + llvm::r#type::pointer(context, 0), + &[ + llvm::r#type::pointer(context, 0), + IntegerType::new(context, 64).into(), + ], + false, + )), Region::new(), &[( Identifier::new(context, "sym_visibility"), @@ -44,12 +39,14 @@ impl ReallocBindingsMeta { )], Location::unknown(context), )); - module.body().append_operation(func::func( + module.body().append_operation(llvm::func( context, StringAttribute::new(context, "free"), - TypeAttribute::new( - FunctionType::new(context, &[llvm::r#type::pointer(context, 0)], &[]).into(), - ), + TypeAttribute::new(llvm::r#type::function( + llvm::r#type::void(context), + &[llvm::r#type::pointer(context, 0)], + false, + )), Region::new(), &[( Identifier::new(context, "sym_visibility"), @@ -58,9 +55,7 @@ impl ReallocBindingsMeta { Location::unknown(context), )); - Self { - phantom: PhantomData, - } + Self } /// Calls the `realloc` function, returns a op with 1 result: an opaque pointer. @@ -70,13 +65,15 @@ impl ReallocBindingsMeta { len: Value<'c, 'a>, location: Location<'c>, ) -> Operation<'c> { - func::call( - context, - FlatSymbolRefAttribute::new(context, "realloc"), - &[ptr, len], - &[llvm::r#type::pointer(context, 0)], - location, - ) + OperationBuilder::new("llvm.call", location) + .add_attributes(&[( + Identifier::new(context, "callee"), + FlatSymbolRefAttribute::new(context, "realloc").into(), + )]) + .add_operands(&[ptr, len]) + .add_results(&[llvm::r#type::pointer(context, 0)]) + .build() + .unwrap() } /// Calls the `free` function. @@ -85,12 +82,13 @@ impl ReallocBindingsMeta { ptr: Value<'c, '_>, location: Location<'c>, ) -> Operation<'c> { - func::call( - context, - FlatSymbolRefAttribute::new(context, "free"), - &[ptr], - &[], - location, - ) + OperationBuilder::new("llvm.call", location) + .add_attributes(&[( + Identifier::new(context, "callee"), + FlatSymbolRefAttribute::new(context, "free").into(), + )]) + .add_operands(&[ptr]) + .build() + .unwrap() } } diff --git a/src/metadata/runtime_bindings.rs b/src/metadata/runtime_bindings.rs index 5e6ba6ace..3ddbf0441 100644 --- a/src/metadata/runtime_bindings.rs +++ b/src/metadata/runtime_bindings.rs @@ -5,11 +5,11 @@ use crate::{error::Result, utils::BlockExt}; use melior::{ - dialect::{func, llvm}, + dialect::{func, llvm, ods}, ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, r#type::{FunctionType, IntegerType}, - Block, Identifier, Location, Module, OperationRef, Region, Value, + Attribute, Block, Identifier, Location, Module, OperationRef, Region, Value, }, Context, }; @@ -76,10 +76,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -128,10 +134,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -178,10 +190,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -220,10 +238,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -262,10 +286,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -299,10 +329,16 @@ impl RuntimeBindingsMeta { FunctionType::new(context, &[llvm::r#type::pointer(context, 0)], &[]).into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -345,10 +381,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -394,10 +436,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -442,10 +490,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], location, )); } @@ -472,7 +526,7 @@ impl RuntimeBindingsMeta { module: &Module, block: &'a Block<'c>, location: Location<'c>, - ) -> Result> + ) -> Result> where 'c: 'a, { @@ -481,24 +535,45 @@ impl RuntimeBindingsMeta { context, StringAttribute::new(context, "cairo_native__dict_new"), TypeAttribute::new( - FunctionType::new(context, &[], &[llvm::r#type::pointer(context, 0)]).into(), + FunctionType::new( + context, + &[llvm::r#type::pointer(context, 0)], + &[llvm::r#type::pointer(context, 0)], + ) + .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } - Ok(block.append_operation(func::call( + let free_fn = block.append_op_result( + ods::llvm::mlir_addressof( + context, + llvm::r#type::pointer(context, 0), + FlatSymbolRefAttribute::new(context, "free"), + location, + ) + .into(), + )?; + + block.append_op_result(func::call( context, FlatSymbolRefAttribute::new(context, "cairo_native__dict_new"), - &[], + &[free_fn], &[llvm::r#type::pointer(context, 0)], location, - ))) + )) } /// Register if necessary, then invoke the `dict_alloc_new()` function. @@ -533,10 +608,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -589,10 +670,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -620,7 +707,7 @@ impl RuntimeBindingsMeta { dict_ptr: Value<'c, 'a>, // ptr to the dict key_ptr: Value<'c, 'a>, // key must be a ptr to Felt location: Location<'c>, - ) -> Result> + ) -> Result> where 'c: 'a, { @@ -640,21 +727,27 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } - Ok(block.append_operation(func::call( + block.append_op_result(func::call( context, FlatSymbolRefAttribute::new(context, "cairo_native__dict_get"), &[dict_ptr, key_ptr], &[llvm::r#type::pointer(context, 0)], location, - ))) + )) } /// Register if necessary, then invoke the `dict_insert()` function. @@ -693,10 +786,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -740,10 +839,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -794,10 +899,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } diff --git a/src/starknet.rs b/src/starknet.rs index b1ef5e17e..5371e0364 100644 --- a/src/starknet.rs +++ b/src/starknet.rs @@ -1,5 +1,6 @@ //! Starknet related code for `cairo_native` +use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; pub type SyscallResult = std::result::Result>; @@ -13,14 +14,58 @@ pub struct ArrayAbi { pub capacity: u32, } +impl From<&ArrayAbi> for Vec { + fn from(value: &ArrayAbi) -> Self { + unsafe { + let since_offset = value.since as usize; + let until_offset = value.until as usize; + debug_assert!(since_offset <= until_offset); + let len = until_offset - since_offset; + match len { + 0 => &[], + _ => std::slice::from_raw_parts(value.ptr.add(since_offset), len), + } + } + .iter() + .map(Felt::from) + .collect() + } +} + /// Binary representation of a `Felt` (in MLIR). -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] #[repr(C, align(16))] pub struct Felt252Abi(pub [u8; 32]); + +impl From for Felt { + fn from(mut value: Felt252Abi) -> Felt { + value.0[31] &= 0x0F; + Felt::from_bytes_le(&value.0) + } +} + +impl From<&Felt252Abi> for Felt { + fn from(value: &Felt252Abi) -> Felt { + let mut value = *value; + value.0[31] &= 0x0F; + Felt::from_bytes_le(&value.0) + } +} + /// Binary representation of a `u256` (in MLIR). // TODO: This shouldn't need to be public. #[derive( - Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + serde::Serialize, + serde::Deserialize, + Default, )] #[repr(C, align(16))] pub struct U256 { @@ -100,16 +145,40 @@ pub struct TxInfo { pub nonce: Felt, } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize, Default)] +#[repr(C, align(16))] pub struct Secp256k1Point { pub x: U256, pub y: U256, + pub is_infinity: bool, +} + +impl Secp256k1Point { + pub fn new(x_lo: u128, x_hi: u128, y_lo: u128, y_hi: u128, is_infinity: bool) -> Self { + Self { + x: U256 { lo: x_lo, hi: x_hi }, + y: U256 { lo: y_lo, hi: y_hi }, + is_infinity, + } + } } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize, Default)] +#[repr(C, align(16))] pub struct Secp256r1Point { pub x: U256, pub y: U256, + pub is_infinity: bool, +} + +impl Secp256r1Point { + pub fn new(x_lo: u128, x_hi: u128, y_lo: u128, y_hi: u128, is_infinity: bool) -> Self { + Self { + x: U256 { lo: x_lo, hi: x_hi }, + y: U256 { lo: y_lo, hi: y_hi }, + is_infinity, + } + } } pub trait StarknetSyscallHandler { @@ -251,10 +320,10 @@ pub trait StarknetSyscallHandler { fn sha256_process_block( &mut self, - prev_state: &[u32; 8], - current_block: &[u32; 16], + state: &mut [u32; 8], + block: &[u32; 16], remaining_gas: &mut u128, - ) -> SyscallResult<[u32; 8]>; + ) -> SyscallResult<()>; #[cfg(feature = "with-cheatcode")] fn cheatcode(&mut self, _selector: Felt, _input: &[Felt]) -> Vec { @@ -450,10 +519,10 @@ impl StarknetSyscallHandler for DummySyscallHandler { fn sha256_process_block( &mut self, - _prev_state: &[u32; 8], - _current_block: &[u32; 16], + _state: &mut [u32; 8], + _block: &[u32; 16], _remaining_gas: &mut u128, - ) -> SyscallResult<[u32; 8]> { + ) -> SyscallResult<()> { unimplemented!() } } @@ -461,8 +530,10 @@ impl StarknetSyscallHandler for DummySyscallHandler { // TODO: Move to the correct place or remove if unused. pub(crate) mod handler { use super::*; + use crate::utils::{libc_free, libc_malloc}; use std::{ alloc::Layout, + ffi::c_void, fmt::Debug, mem::{size_of, ManuallyDrop, MaybeUninit}, ptr::{null_mut, NonNull}, @@ -727,8 +798,8 @@ pub(crate) mod handler { result_ptr: &mut SyscallResultAbi<*mut [u32; 8]>, ptr: &mut T, gas: &mut u128, - prev_state: &[u32; 8], - current_block: &[u32; 16], + state: *mut [u32; 8], + block: &[u32; 16], ), // testing syscalls #[cfg(feature = "with-cheatcode")] @@ -818,8 +889,7 @@ pub(crate) mod handler { capacity: 0, }, _ => { - let ptr = - libc::malloc(Layout::array::(data.len()).unwrap().size()) as *mut E; + let ptr = libc_malloc(Layout::array::(data.len()).unwrap().size()) as *mut E; let len: u32 = data.len().try_into().unwrap(); for (i, val) in data.iter().enumerate() { @@ -874,20 +944,15 @@ pub(crate) mod handler { selector: &Felt252Abi, input: &ArrayAbi, ) { - let input: Vec<_> = unsafe { - let since_offset = input.since as usize; - let until_offset = input.until as usize; - debug_assert!(since_offset <= until_offset); - let len = until_offset - since_offset; - std::slice::from_raw_parts(input.ptr.add(since_offset), len) + let selector = Felt::from(selector); + let input_vec: Vec<_> = input.into(); + + unsafe { + libc_free(input.ptr as *mut c_void); } - .iter() - .map(|x| Felt::from_bytes_le(&x.0)) - .collect(); - let selector = Felt::from_bytes_le(&selector.0); let result = ptr - .cheatcode(selector, &input) + .cheatcode(selector, &input_vec) .into_iter() .map(|x| Felt252Abi(x.to_bytes_le())) .collect::>(); @@ -907,20 +972,19 @@ pub(crate) mod handler { ok: ManuallyDrop::new(SyscallResultAbiOk { tag: 0u8, payload: unsafe { - let mut block_info_ptr = - NonNull::new( - libc::malloc(size_of::()) as *mut BlockInfoAbi - ) - .unwrap(); + let mut block_info_ptr = NonNull::new(libc_malloc( + size_of::(), + ) + as *mut BlockInfoAbi) + .unwrap(); block_info_ptr.as_mut().block_number = x.block_info.block_number; block_info_ptr.as_mut().block_timestamp = x.block_info.block_timestamp; block_info_ptr.as_mut().sequencer_address = Felt252Abi(x.block_info.sequencer_address.to_bytes_le()); - let mut tx_info_ptr = NonNull::new( - libc::malloc(size_of::()) as *mut TxInfoAbi, - ) - .unwrap(); + let mut tx_info_ptr = + NonNull::new(libc_malloc(size_of::()) as *mut TxInfoAbi) + .unwrap(); tx_info_ptr.as_mut().version = Felt252Abi(x.tx_info.version.to_bytes_le()); tx_info_ptr.as_mut().account_contract_address = @@ -940,7 +1004,7 @@ pub(crate) mod handler { tx_info_ptr.as_mut().nonce = Felt252Abi(x.tx_info.nonce.to_bytes_le()); let mut execution_info_ptr = - NonNull::new(libc::malloc(size_of::()) + NonNull::new(libc_malloc(size_of::()) as *mut ExecutionInfoAbi) .unwrap(); execution_info_ptr.as_mut().block_info = block_info_ptr; @@ -973,22 +1037,22 @@ pub(crate) mod handler { tag: 0u8, payload: unsafe { let mut execution_info_ptr = - NonNull::new(libc::malloc(size_of::()) + NonNull::new(libc_malloc(size_of::()) as *mut ExecutionInfoV2Abi) .unwrap(); - let mut block_info_ptr = - NonNull::new( - libc::malloc(size_of::()) as *mut BlockInfoAbi - ) - .unwrap(); + let mut block_info_ptr = NonNull::new(libc_malloc( + size_of::(), + ) + as *mut BlockInfoAbi) + .unwrap(); block_info_ptr.as_mut().block_number = x.block_info.block_number; block_info_ptr.as_mut().block_timestamp = x.block_info.block_timestamp; block_info_ptr.as_mut().sequencer_address = Felt252Abi(x.block_info.sequencer_address.to_bytes_le()); let mut tx_info_ptr = NonNull::new( - libc::malloc(size_of::()) as *mut TxInfoV2Abi, + libc_malloc(size_of::()) as *mut TxInfoV2Abi, ) .unwrap(); tx_info_ptr.as_mut().version = @@ -1067,41 +1131,19 @@ pub(crate) mod handler { calldata: &ArrayAbi, deploy_from_zero: bool, ) { - let class_hash = Felt::from_bytes_be(&{ - let mut data = class_hash.0; - data.reverse(); - data - }); - let contract_address_salt = Felt::from_bytes_be(&{ - let mut data = contract_address_salt.0; - data.reverse(); - data - }); - - let calldata: Vec<_> = unsafe { - let since_offset = calldata.since as usize; - let until_offset = calldata.until as usize; - debug_assert!(since_offset <= until_offset); - let len = until_offset - since_offset; - match len { - 0 => &[], - _ => std::slice::from_raw_parts(calldata.ptr.add(since_offset), len), - } + let class_hash = Felt::from(class_hash); + let contract_address_salt = Felt::from(contract_address_salt); + + let calldata_vec: Vec<_> = calldata.into(); + + unsafe { + libc_free(calldata.ptr as *mut c_void); } - .iter() - .map(|x| { - Felt::from_bytes_be(&{ - let mut data = x.0; - data.reverse(); - data - }) - }) - .collect(); let result = ptr.deploy( class_hash, contract_address_salt, - &calldata, + &calldata_vec, deploy_from_zero, gas, ); @@ -1127,11 +1169,7 @@ pub(crate) mod handler { gas: &mut u128, class_hash: &Felt252Abi, ) { - let class_hash = Felt::from_bytes_be(&{ - let mut data = class_hash.0; - data.reverse(); - data - }); + let class_hash = Felt::from(class_hash); let result = ptr.replace_class(class_hash, gas); *result_ptr = match result { @@ -1153,38 +1191,16 @@ pub(crate) mod handler { function_selector: &Felt252Abi, calldata: &ArrayAbi, ) { - let class_hash = Felt::from_bytes_be(&{ - let mut data = class_hash.0; - data.reverse(); - data - }); - let function_selector = Felt::from_bytes_be(&{ - let mut data = function_selector.0; - data.reverse(); - data - }); - - let calldata: Vec<_> = unsafe { - let since_offset = calldata.since as usize; - let until_offset = calldata.until as usize; - debug_assert!(since_offset <= until_offset); - let len = until_offset - since_offset; - match len { - 0 => &[], - _ => std::slice::from_raw_parts(calldata.ptr.add(since_offset), len), - } + let class_hash = Felt::from(class_hash); + let function_selector = Felt::from(function_selector); + + let calldata_vec: Vec = calldata.into(); + + unsafe { + libc_free(calldata.ptr as *mut c_void); } - .iter() - .map(|x| { - Felt::from_bytes_be(&{ - let mut data = x.0; - data.reverse(); - data - }) - }) - .collect(); - - let result = ptr.library_call(class_hash, function_selector, &calldata, gas); + + let result = ptr.library_call(class_hash, function_selector, &calldata_vec, gas); *result_ptr = match result { Ok(x) => { @@ -1209,38 +1225,16 @@ pub(crate) mod handler { entry_point_selector: &Felt252Abi, calldata: &ArrayAbi, ) { - let address = Felt::from_bytes_be(&{ - let mut data = address.0; - data.reverse(); - data - }); - let entry_point_selector = Felt::from_bytes_be(&{ - let mut data = entry_point_selector.0; - data.reverse(); - data - }); - - let calldata: Vec<_> = unsafe { - let since_offset = calldata.since as usize; - let until_offset = calldata.until as usize; - debug_assert!(since_offset <= until_offset); - let len = until_offset - since_offset; - match len { - 0 => &[], - _ => std::slice::from_raw_parts(calldata.ptr.add(since_offset), len), - } + let address = Felt::from(address); + let entry_point_selector = Felt::from(entry_point_selector); + + let calldata_vec: Vec = calldata.into(); + + unsafe { + libc_free(calldata.ptr as *mut c_void); } - .iter() - .map(|x| { - Felt::from_bytes_be(&{ - let mut data = x.0; - data.reverse(); - data - }) - }) - .collect(); - - let result = ptr.call_contract(address, entry_point_selector, &calldata, gas); + + let result = ptr.call_contract(address, entry_point_selector, &calldata_vec, gas); *result_ptr = match result { Ok(x) => { @@ -1264,11 +1258,7 @@ pub(crate) mod handler { address_domain: u32, address: &Felt252Abi, ) { - let address = Felt::from_bytes_be(&{ - let mut data = address.0; - data.reverse(); - data - }); + let address = Felt::from(address); let result = ptr.storage_read(address_domain, address, gas); *result_ptr = match result { @@ -1290,16 +1280,8 @@ pub(crate) mod handler { address: &Felt252Abi, value: &Felt252Abi, ) { - let address = Felt::from_bytes_be(&{ - let mut data = address.0; - data.reverse(); - data - }); - let value = Felt::from_bytes_be(&{ - let mut data = value.0; - data.reverse(); - data - }); + let address = Felt::from(address); + let value = Felt::from(value); let result = ptr.storage_write(address_domain, address, value, gas); *result_ptr = match result { @@ -1320,47 +1302,19 @@ pub(crate) mod handler { keys: &ArrayAbi, data: &ArrayAbi, ) { - let keys: Vec<_> = unsafe { - let since_offset = keys.since as usize; - let until_offset = keys.until as usize; - debug_assert!(since_offset <= until_offset); - let len = until_offset - since_offset; - match len { - 0 => &[], - _ => std::slice::from_raw_parts(keys.ptr.add(since_offset), len), - } + let keys_vec: Vec<_> = keys.into(); + + unsafe { + libc_free(keys.ptr as *mut c_void); } - .iter() - .map(|x| { - Felt::from_bytes_be(&{ - let mut data = x.0; - data.reverse(); - data - }) - }) - .collect(); - - let data: Vec<_> = unsafe { - let since_offset = data.since as usize; - let until_offset = data.until as usize; - debug_assert!(since_offset <= until_offset); - let len = until_offset - since_offset; - match len { - 0 => &[], - _ => std::slice::from_raw_parts(data.ptr.add(since_offset), len), - } + + let data_vec: Vec<_> = data.into(); + + unsafe { + libc_free(data.ptr as *mut c_void); } - .iter() - .map(|x| { - Felt::from_bytes_be(&{ - let mut data = x.0; - data.reverse(); - data - }) - }) - .collect(); - - let result = ptr.emit_event(&keys, &data, gas); + + let result = ptr.emit_event(&keys_vec, &data_vec, gas); *result_ptr = match result { Ok(_) => SyscallResultAbi { @@ -1380,32 +1334,14 @@ pub(crate) mod handler { to_address: &Felt252Abi, payload: &ArrayAbi, ) { - let to_address = Felt::from_bytes_be(&{ - let mut data = to_address.0; - data.reverse(); - data - }); - let payload: Vec<_> = unsafe { - let since_offset = payload.since as usize; - let until_offset = payload.until as usize; - debug_assert!(since_offset <= until_offset); - let len = until_offset - since_offset; - match len { - 0 => &[], - _ => std::slice::from_raw_parts(payload.ptr.add(since_offset), len), - } + let to_address = Felt::from(to_address); + let payload_vec: Vec<_> = payload.into(); + + unsafe { + libc_free(payload.ptr as *mut c_void); } - .iter() - .map(|x| { - Felt::from_bytes_be(&{ - let mut data = x.0; - data.reverse(); - data - }) - }) - .collect(); - - let result = ptr.send_message_to_l1(to_address, &payload, gas); + + let result = ptr.send_message_to_l1(to_address, &payload_vec, gas); *result_ptr = match result { Ok(_) => SyscallResultAbi { @@ -1424,7 +1360,7 @@ pub(crate) mod handler { gas: &mut u128, input: &ArrayAbi, ) { - let input = unsafe { + let input_vec = unsafe { let since_offset = input.since as usize; let until_offset = input.until as usize; debug_assert!(since_offset <= until_offset); @@ -1435,7 +1371,10 @@ pub(crate) mod handler { } }; - let result = ptr.keccak(input, gas); + let result = ptr.keccak(input_vec, gas); + unsafe { + libc_free(input.ptr as *mut c_void); + } *result_ptr = match result { Ok(x) => SyscallResultAbi { @@ -1681,26 +1620,17 @@ pub(crate) mod handler { result_ptr: &mut SyscallResultAbi<*mut [u32; 8]>, ptr: &mut T, gas: &mut u128, - prev_state: &[u32; 8], - current_block: &[u32; 16], + state: *mut [u32; 8], + block: &[u32; 16], ) { - let result = ptr.sha256_process_block(prev_state, current_block, gas); + let state_ref = unsafe { state.as_mut().unwrap() }; + let result = ptr.sha256_process_block(state_ref, block, gas); *result_ptr = match result { Ok(x) => SyscallResultAbi { ok: ManuallyDrop::new(SyscallResultAbiOk { tag: 0u8, - payload: ManuallyDrop::new({ - unsafe { - let data = libc::malloc(std::mem::size_of_val(&x)).cast(); - std::ptr::copy_nonoverlapping::( - x.as_ptr().cast(), - data, - x.len(), - ); - data.cast() - } - }), + payload: ManuallyDrop::new(state), }), }, Err(e) => Self::wrap_error(&e), diff --git a/src/starknet_stub.rs b/src/starknet_stub.rs index 82b6a860d..bf25d22c1 100644 --- a/src/starknet_stub.rs +++ b/src/starknet_stub.rs @@ -2,18 +2,18 @@ use std::{ collections::{HashMap, VecDeque}, - iter::once, + fmt, }; use crate::starknet::{ BlockInfo, ExecutionInfo, ExecutionInfoV2, Secp256k1Point, Secp256r1Point, StarknetSyscallHandler, SyscallResult, TxInfo, TxV2Info, U256, }; -use k256::elliptic_curve::{ - generic_array::GenericArray, - sec1::{FromEncodedPoint, ToEncodedPoint}, -}; -use sec1::point::Coordinates; +use ark_ec::short_weierstrass::{Affine, Projective, SWCurveConfig}; +use ark_ff::{BigInt, PrimeField}; +use itertools::Itertools; +use num_bigint::BigUint; +use num_traits::Zero; use starknet_types_core::felt::Felt; use tracing::instrument; @@ -78,6 +78,191 @@ pub struct ContractLogs { type L2ToL1Message = (Felt, Vec); +#[derive(PartialEq, Clone, Copy)] +struct Secp256Point(Affine); + +impl fmt::Debug for Secp256Point { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Secp256Point").field(&self.0).finish() + } +} + +impl From> for Secp256k1Point { + fn from(Secp256Point(Affine { x, y, infinity }): Secp256Point) -> Self { + Secp256k1Point { + x: big4int_to_u256(x.into()), + y: big4int_to_u256(y.into()), + is_infinity: infinity, + } + } +} + +impl From> for Secp256r1Point { + fn from(Secp256Point(Affine { x, y, infinity }): Secp256Point) -> Self { + Secp256r1Point { + x: big4int_to_u256(x.into()), + y: big4int_to_u256(y.into()), + is_infinity: infinity, + } + } +} + +impl From for Secp256Point { + fn from(p: Secp256k1Point) -> Self { + Secp256Point(Affine { + x: u256_to_biguint(p.x).into(), + y: u256_to_biguint(p.y).into(), + infinity: p.is_infinity, + }) + } +} + +impl From for Secp256Point { + fn from(p: Secp256r1Point) -> Self { + Secp256Point(Affine { + x: u256_to_biguint(p.x).into(), + y: u256_to_biguint(p.y).into(), + infinity: p.is_infinity, + }) + } +} + +pub fn u256_to_biguint(u256: U256) -> BigUint { + let lo = BigUint::from(u256.lo); + let hi = BigUint::from(u256.hi); + + (hi << 128) + lo +} + +pub fn big4int_to_u256(b_int: BigInt<4>) -> U256 { + let [a, b, c, d] = b_int.0; + + let lo = u128::from(a) | (u128::from(b) << 64); + let hi = u128::from(c) | (u128::from(d) << 64); + + U256 { lo, hi } +} + +pub fn encode_str_as_felts(msg: &str) -> Vec { + const CHUNK_SIZE: usize = 32; + + let data = msg.as_bytes().chunks(CHUNK_SIZE - 1); + let mut encoding = vec![Felt::default(); data.len()]; + for (i, data_chunk) in data.enumerate() { + let mut chunk = [0_u8; CHUNK_SIZE]; + chunk[1..data_chunk.len() + 1].copy_from_slice(data_chunk); + encoding[i] = Felt::from_bytes_be(&chunk); + } + encoding +} + +pub fn decode_felts_as_str(encoding: &[Felt]) -> String { + let bytes_err: Vec<_> = encoding + .iter() + .flat_map(|felt| felt.to_bytes_be()[1..32].to_vec()) + .collect(); + + match String::from_utf8(bytes_err) { + Ok(s) => s.trim_matches('\0').to_owned(), + Err(_) => { + let err_msgs = encoding + .iter() + .map( + |felt| match String::from_utf8(felt.to_bytes_be()[1..32].to_vec()) { + Ok(s) => format!("{} ({})", s.trim_matches('\0'), felt), + Err(_) => felt.to_string(), + }, + ) + .join(", "); + format!("[{}]", err_msgs) + } + } +} + +impl Secp256Point +where + Curve::BaseField: PrimeField, // constraint for get_point_by_id +{ + // Given a (x,y) pair it will + // - return the point at infinity for (0,0) + // - Err if either x or y is outside of the modulus + // - Ok(None) if (x,y) are within the modules but not on the curve + // - Ok(Some(Point)) if (x,y) are on the curve + fn new(x: U256, y: U256) -> Result, Vec> { + let x = u256_to_biguint(x); + let y = u256_to_biguint(y); + let modulos = Curve::BaseField::MODULUS.into(); + + if x >= modulos || y >= modulos { + let error = Felt::from_hex( + "0x00000000000000000000000000000000496e76616c696420617267756d656e74", + ) // INVALID_ARGUMENT + .map_err(|err| encode_str_as_felts(&err.to_string()))?; + + return Err(vec![error]); + } + + Ok(maybe_affine(x.into(), y.into())) + } + + fn add(p0: Self, p1: Self) -> Self { + let result: Projective = p0.0 + p1.0; + Secp256Point(result.into()) + } + + fn mul(p: Self, m: U256) -> Self { + let result = p.0 * Curve::ScalarField::from(u256_to_biguint(m)); + Secp256Point(result.into()) + } + + fn get_point_from_x(x: U256, y_parity: bool) -> Result, Vec> { + let modulos = Curve::BaseField::MODULUS.into(); + let x = u256_to_biguint(x); + + if x >= modulos { + let error = Felt::from_hex( + "0x00000000000000000000000000000000496e76616c696420617267756d656e74", + ) // INVALID_ARGUMENT + .map_err(|err| encode_str_as_felts(&err.to_string()))?; + + return Err(vec![error]); + } + + let x = x.into(); + let maybe_ec_point = Affine::::get_ys_from_x_unchecked(x) + .map(|(smaller, greater)| { + // Return the correct y coordinate based on the parity. + if ark_ff::BigInteger::is_odd(&smaller.into_bigint()) == y_parity { + smaller + } else { + greater + } + }) + .map(|y| Affine::::new_unchecked(x, y)) + .filter(|p| p.is_in_correct_subgroup_assuming_on_curve()); + + Ok(maybe_ec_point.map(Secp256Point)) + } +} + +/// Variation on [`Affine::new`] that doesn't panic and maps (x,y) = (0,0) -> infinity +fn maybe_affine( + x: Curve::BaseField, + y: Curve::BaseField, +) -> Option> { + let ec_point = if x.is_zero() && y.is_zero() { + Affine::::identity() + } else { + Affine::::new_unchecked(x, y) + }; + + if ec_point.is_on_curve() && ec_point.is_in_correct_subgroup_assuming_on_curve() { + Some(Secp256Point(ec_point)) + } else { + None + } +} + impl StarknetSyscallHandler for &mut StubSyscallHandler { #[instrument(skip(self))] fn get_block_hash( @@ -228,38 +413,30 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { #[instrument(skip(self))] fn keccak(&mut self, input: &[u64], gas: &mut u128) -> SyscallResult { - tracing::debug!("called"); + const KECCAK_FULL_RATE_IN_WORDS: usize = 17; + let length = input.len(); + let (_n_rounds, remainder) = num_integer::div_rem(length, KECCAK_FULL_RATE_IN_WORDS); - if length % 17 != 0 { - let error_msg = b"Invalid keccak input size"; - let felt_error = Felt::from_bytes_be_slice(error_msg); - return Err(vec![felt_error]); + if remainder != 0 { + // In VM this error is wrapped into `SyscallExecutionError::SyscallError` + return Err(vec![Felt::from_hex( + "0x000000000000000000000000496e76616c696420696e707574206c656e677468", + ) + .unwrap()]); } - let n_chunks = length / 17; let mut state = [0u64; 25]; - - for i in 0..n_chunks { - if *gas < KECCAK_ROUND_COST { - let error_msg = b"Syscall out of gas"; - let felt_error = Felt::from_bytes_be_slice(error_msg); - return Err(vec![felt_error]); - } - const KECCAK_ROUND_COST: u128 = 180000; - *gas -= KECCAK_ROUND_COST; - let chunk = &input[i * 17..(i + 1) * 17]; //(request.input_start + i * 17)?; + for chunk in input.chunks(KECCAK_FULL_RATE_IN_WORDS) { for (i, val) in chunk.iter().enumerate() { state[i] ^= val; } keccak::f1600(&mut state) } - // state[0] and state[1] conform the hash_high (u128) - // state[2] and state[3] conform the hash_low (u128) - SyscallResult::Ok(U256 { - lo: state[0] as u128 | ((state[1] as u128) << 64), - hi: state[2] as u128 | ((state[3] as u128) << 64), + Ok(U256 { + hi: u128::from(state[2]) | (u128::from(state[3]) << 64), + lo: u128::from(state[0]) | (u128::from(state[1]) << 64), }) } @@ -270,28 +447,7 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { y: U256, _remaining_gas: &mut u128, ) -> SyscallResult> { - tracing::debug!("called"); - // The following unwraps should be unreachable because the iterator we provide has the - // expected number of bytes. - let point = k256::ProjectivePoint::from_encoded_point( - &k256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - x.hi.to_be_bytes().into_iter().chain(x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - y.hi.to_be_bytes().into_iter().chain(y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ); - - if bool::from(point.is_some()) { - Ok(Some(Secp256k1Point { x, y })) - } else { - Ok(None) - } + Secp256Point::new(x, y).map(|op| op.map(|p| p.into())) } #[instrument(skip(self))] @@ -302,76 +458,8 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { _remaining_gas: &mut u128, ) -> SyscallResult { tracing::debug!("called"); - // The inner unwraps should be unreachable because the iterator we provide has the expected - // number of bytes. The outer unwraps depend on the felt values, which should be valid since - // they'll be provided by secp256 syscalls. - let p0 = k256::ProjectivePoint::from_encoded_point( - &k256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - p0.x.hi - .to_be_bytes() - .into_iter() - .chain(p0.x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - p0.y.hi - .to_be_bytes() - .into_iter() - .chain(p0.y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ) - .unwrap(); - let p1 = k256::ProjectivePoint::from_encoded_point( - &k256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - p1.x.hi - .to_be_bytes() - .into_iter() - .chain(p1.x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - p1.y.hi - .to_be_bytes() - .into_iter() - .chain(p1.y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ) - .unwrap(); - - let p = p0 + p1; - - let p = p.to_encoded_point(false); - let (x, y) = match p.coordinates() { - Coordinates::Uncompressed { x, y } => (x, y), - _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. - unreachable!() - } - }; - // The following two unwraps should be safe because the array always has 32 bytes. The other - // four are definitely safe because the slicing guarantees its length to be the right one. - let x: [u8; 32] = x.as_slice().try_into().unwrap(); - let y: [u8; 32] = y.as_slice().try_into().unwrap(); - Ok(Secp256k1Point { - x: U256 { - hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), - }, - y: U256 { - hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), - }, - }) + Ok(Secp256Point::add(p0.into(), p1.into()).into()) } #[instrument(skip(self))] @@ -381,62 +469,7 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { m: U256, _remaining_gas: &mut u128, ) -> SyscallResult { - // The inner unwrap should be unreachable because the iterator we provide has the expected - // number of bytes. The outer unwrap depends on the felt values, which should be valid since - // they'll be provided by secp256 syscalls. - let p = k256::ProjectivePoint::from_encoded_point( - &k256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - p.x.hi.to_be_bytes().into_iter().chain(p.x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - p.y.hi.to_be_bytes().into_iter().chain(p.y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ) - .unwrap(); - let m: k256::Scalar = k256::elliptic_curve::ScalarPrimitive::from_slice(&{ - let mut buf = [0u8; 32]; - buf[0..16].copy_from_slice(&m.hi.to_be_bytes()); - buf[16..32].copy_from_slice(&m.lo.to_be_bytes()); - buf - }) - .map_err(|_| { - vec![Felt::from_bytes_be( - b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0invalid scalar", - )] - })? - .into(); - - let p = p * m; - - let p = p.to_encoded_point(false); - let (x, y) = match p.coordinates() { - Coordinates::Uncompressed { x, y } => (x, y), - _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. - unreachable!() - } - }; - - // The following two unwraps should be safe because the array always has 32 bytes. The other - // four are definitely safe because the slicing guarantees its length to be the right one. - let x: [u8; 32] = x.as_slice().try_into().unwrap(); - let y: [u8; 32] = y.as_slice().try_into().unwrap(); - Ok(Secp256k1Point { - x: U256 { - hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), - }, - y: U256 { - hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), - }, - }) + Ok(Secp256Point::mul(p.into(), m).into()) } #[instrument(skip(self))] @@ -446,50 +479,7 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { y_parity: bool, _remaining_gas: &mut u128, ) -> SyscallResult> { - tracing::debug!("called"); - // The inner unwrap should be unreachable because the iterator we provide has the expected - // number of bytes. The outer unwrap depends on the encoding format, which should be valid - // since it's hardcoded.. - let point = k256::ProjectivePoint::from_encoded_point( - &k256::EncodedPoint::from_bytes( - k256::CompressedPoint::from_exact_iter( - once(0x02 | y_parity as u8) - .chain(x.hi.to_be_bytes()) - .chain(x.lo.to_be_bytes()), - ) - .unwrap(), - ) - .unwrap(), - ); - - if bool::from(point.is_some()) { - // This unwrap has already been checked in the `if` expression's condition. - let p = point.unwrap(); - - let p = p.to_encoded_point(false); - let y = match p.coordinates() { - Coordinates::Uncompressed { y, .. } => y, - _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. - unreachable!() - } - }; - - // The following unwrap should be safe because the array always has 32 bytes. The other - // two are definitely safe because the slicing guarantees its length to be the right - // one. - let y: [u8; 32] = y.as_slice().try_into().unwrap(); - Ok(Some(Secp256k1Point { - x, - y: U256 { - hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), - }, - })) - } else { - Ok(None) - } + Secp256Point::get_point_from_x(x, y_parity).map(|op| op.map(|p| p.into())) } #[instrument(skip(self))] @@ -498,7 +488,6 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { p: Secp256k1Point, _remaining_gas: &mut u128, ) -> SyscallResult<(U256, U256)> { - tracing::debug!("called"); Ok((p.x, p.y)) } @@ -509,28 +498,7 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { y: U256, _remaining_gas: &mut u128, ) -> SyscallResult> { - tracing::debug!("called"); - // The following unwraps should be unreachable because the iterator we provide has the - // expected number of bytes. - let point = p256::ProjectivePoint::from_encoded_point( - &k256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - x.hi.to_be_bytes().into_iter().chain(x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - y.hi.to_be_bytes().into_iter().chain(y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ); - - if bool::from(point.is_some()) { - Ok(Some(Secp256r1Point { x, y })) - } else { - Ok(None) - } + Secp256Point::new(x, y).map(|op| op.map(|p| p.into())) } #[instrument(skip(self))] @@ -540,77 +508,7 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { p1: Secp256r1Point, _remaining_gas: &mut u128, ) -> SyscallResult { - tracing::debug!("called"); - // The inner unwraps should be unreachable because the iterator we provide has the expected - // number of bytes. The outer unwraps depend on the felt values, which should be valid since - // they'll be provided by secp256 syscalls. - let p0 = p256::ProjectivePoint::from_encoded_point( - &p256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - p0.x.hi - .to_be_bytes() - .into_iter() - .chain(p0.x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - p0.y.hi - .to_be_bytes() - .into_iter() - .chain(p0.y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ) - .unwrap(); - let p1 = p256::ProjectivePoint::from_encoded_point( - &p256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - p1.x.hi - .to_be_bytes() - .into_iter() - .chain(p1.x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - p1.y.hi - .to_be_bytes() - .into_iter() - .chain(p1.y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ) - .unwrap(); - - let p = p0 + p1; - - let p = p.to_encoded_point(false); - let (x, y) = match p.coordinates() { - Coordinates::Uncompressed { x, y } => (x, y), - _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. - unreachable!() - } - }; - - // The following two unwraps should be safe because the array always has 32 bytes. The other - // four are definitely safe because the slicing guarantees its length to be the right one. - let x: [u8; 32] = x.as_slice().try_into().unwrap(); - let y: [u8; 32] = y.as_slice().try_into().unwrap(); - Ok(Secp256r1Point { - x: U256 { - hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), - }, - y: U256 { - hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), - }, - }) + Ok(Secp256Point::add(p0.into(), p1.into()).into()) } #[instrument(skip(self))] @@ -620,61 +518,7 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { m: U256, _remaining_gas: &mut u128, ) -> SyscallResult { - // The inner unwrap should be unreachable because the iterator we provide has the expected - // number of bytes. The outer unwrap depends on the felt values, which should be valid since - // they'll be provided by secp256 syscalls. - let p = p256::ProjectivePoint::from_encoded_point( - &p256::EncodedPoint::from_affine_coordinates( - &GenericArray::from_exact_iter( - p.x.hi.to_be_bytes().into_iter().chain(p.x.lo.to_be_bytes()), - ) - .unwrap(), - &GenericArray::from_exact_iter( - p.y.hi.to_be_bytes().into_iter().chain(p.y.lo.to_be_bytes()), - ) - .unwrap(), - false, - ), - ) - .unwrap(); - let m: p256::Scalar = p256::elliptic_curve::ScalarPrimitive::from_slice(&{ - let mut buf = [0u8; 32]; - buf[0..16].copy_from_slice(&m.hi.to_be_bytes()); - buf[16..32].copy_from_slice(&m.lo.to_be_bytes()); - buf - }) - .map_err(|_| { - vec![Felt::from_bytes_be( - b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0invalid scalar", - )] - })? - .into(); - - let p = p * m; - let p = p.to_encoded_point(false); - let (x, y) = match p.coordinates() { - Coordinates::Uncompressed { x, y } => (x, y), - _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. - unreachable!() - } - }; - - // The following two unwraps should be safe because the array always has 32 bytes. The other - // four are definitely safe because the slicing guarantees its length to be the right one. - let x: [u8; 32] = x.as_slice().try_into().unwrap(); - let y: [u8; 32] = y.as_slice().try_into().unwrap(); - Ok(Secp256r1Point { - x: U256 { - hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), - }, - y: U256 { - hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), - }, - }) + Ok(Secp256Point::mul(p.into(), m).into()) } #[instrument(skip(self))] @@ -684,38 +528,7 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { y_parity: bool, _remaining_gas: &mut u128, ) -> SyscallResult> { - let point = p256::ProjectivePoint::from_encoded_point( - &p256::EncodedPoint::from_bytes( - p256::CompressedPoint::from_exact_iter( - once(0x02 | y_parity as u8) - .chain(x.hi.to_be_bytes()) - .chain(x.lo.to_be_bytes()), - ) - .unwrap(), - ) - .unwrap(), - ); - - if bool::from(point.is_some()) { - let p = point.unwrap(); - - let p = p.to_encoded_point(false); - let y = match p.coordinates() { - Coordinates::Uncompressed { y, .. } => y, - _ => unreachable!(), - }; - - let y: [u8; 32] = y.as_slice().try_into().unwrap(); - Ok(Some(Secp256r1Point { - x, - y: U256 { - hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), - lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), - }, - })) - } else { - Ok(None) - } + Secp256Point::get_point_from_x(x, y_parity).map(|op| op.map(|p| p.into())) } #[instrument(skip(self))] @@ -724,7 +537,6 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { p: Secp256r1Point, _remaining_gas: &mut u128, ) -> SyscallResult<(U256, U256)> { - tracing::debug!("called"); Ok((p.x, p.y)) } @@ -822,19 +634,18 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { fn sha256_process_block( &mut self, - prev_state: &[u32; 8], - current_block: &[u32; 16], + state: &mut [u32; 8], + block: &[u32; 16], _remaining_gas: &mut u128, - ) -> SyscallResult<[u32; 8]> { + ) -> SyscallResult<()> { // reference impl // https://github.com/starkware-libs/cairo/blob/ba3f82b4a09972b6a24bf791e344cabce579bf69/crates/cairo-lang-runner/src/casm_run/mod.rs#L1292 - let mut state = *prev_state; let data_as_bytes = sha2::digest::generic_array::GenericArray::from_exact_iter( - current_block.iter().flat_map(|x| x.to_be_bytes()), + block.iter().flat_map(|x| x.to_be_bytes()), ) .unwrap(); - sha2::compress256(&mut state, &[data_as_bytes]); - Ok(state) + sha2::compress256(state, &[data_as_bytes]); + Ok(()) } } @@ -853,6 +664,7 @@ mod tests { hi: 75181762170223969696219813306313470806, lo: 134255467439736302886468555755295925874, }, + is_infinity: false, }; let mut test_syscall_handler = StubSyscallHandler::default(); @@ -889,7 +701,11 @@ mod tests { assert_eq!( test_syscall_handler.secp256k1_new(x, y, &mut 10).unwrap(), - Some(Secp256k1Point { x, y }) + Some(Secp256k1Point { + x, + y, + is_infinity: false + }) ); } @@ -924,6 +740,7 @@ mod tests { lo: 336417762351022071123394393598455764152, hi: 96009999919712310848645357523629574312, }, + is_infinity: false, }; let p2 = p1; @@ -940,6 +757,7 @@ mod tests { lo: 329597642124196932058042157271922763050, hi: 35730324229579385338853513728577301230, }, + is_infinity: false, }; assert_eq!(p3, p1_double); assert_eq!( @@ -959,6 +777,7 @@ mod tests { lo: 134255467439736302886468555755295925874, hi: 75181762170223969696219813306313470806, }, + is_infinity: false, }; assert_eq!( test_syscall_handler.secp256k1_add(p1, p3, &mut 10).unwrap(), @@ -998,6 +817,7 @@ mod tests { lo: 68974579539311638391577168388077592842, hi: 26163136114030451075775058782541084873, }, + is_infinity: false } ); } @@ -1028,6 +848,7 @@ mod tests { lo: 271307787381626825071797439039395650341, hi: 314119230806908012387599548649227126582, }, + is_infinity: false } ); } @@ -1062,22 +883,29 @@ mod tests { .secp256r1_new(x, y, &mut 10) .unwrap() .unwrap(), - Secp256r1Point { x, y } + Secp256r1Point { + x, + y, + is_infinity: false + } ); } #[test] - fn test_secp256r1_new_none() { + fn test_secp256r1_new_infinity() { let mut test_syscall_handler = StubSyscallHandler::default(); let mut test_syscall_handler = &mut test_syscall_handler; let x = U256 { hi: 0, lo: 0 }; let y = U256 { hi: 0, lo: 0 }; - assert!(test_syscall_handler - .secp256r1_new(x, y, &mut 10) - .unwrap() - .is_none()); + assert!( + test_syscall_handler + .secp256r1_new(x, y, &mut 10) + .unwrap() + .unwrap() + .is_infinity + ); } #[test] @@ -1094,6 +922,7 @@ mod tests { lo: 111045440647474106186537215379882575585, hi: 118910939004298029402109603132816090461, }, + is_infinity: false, }; let p2 = p1; @@ -1110,6 +939,7 @@ mod tests { lo: 231570843221643745062297421862629788481, hi: 84249534056490759701994051847937833933, }, + is_infinity: false, }; assert_eq!(p3, p1_double); assert_eq!( @@ -1129,6 +959,7 @@ mod tests { lo: 282344931843342117515389970197013120959, hi: 178681203065513270100417145499857169664, }, + is_infinity: false, }; assert_eq!( test_syscall_handler.secp256r1_add(p1, p3, &mut 10).unwrap(), @@ -1162,7 +993,11 @@ mod tests { .secp256r1_get_point_from_x(x, true, &mut 10) .unwrap() .unwrap(), - Secp256r1Point { x, y } + Secp256r1Point { + x, + y, + is_infinity: false + } ); } @@ -1186,7 +1021,11 @@ mod tests { .secp256r1_get_point_from_x(x, false, &mut 10) .unwrap() .unwrap(), - Secp256r1Point { x, y } + Secp256r1Point { + x, + y, + is_infinity: false + } ); } @@ -1214,6 +1053,7 @@ mod tests { lo: 221371427837412271565447410779117722274, hi: 229236926352692519791101729645429586206, }, + is_infinity: false, }; let mut test_syscall_handler = StubSyscallHandler::default(); diff --git a/src/types.rs b/src/types.rs index 7711c0291..6d0d6314e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -549,10 +549,12 @@ impl TypeBuilder for CoreTypeConcrete { | CoreTypeConcrete::Poseidon(_) | CoreTypeConcrete::RangeCheck96(_) | CoreTypeConcrete::SegmentArena(_) => false, + + // A ptr to a list of costs. + CoreTypeConcrete::BuiltinCosts(_) => false, + // Other builtins: - CoreTypeConcrete::BuiltinCosts(_) - | CoreTypeConcrete::Uint128MulGuarantee(_) - | CoreTypeConcrete::Coupon(_) => true, + CoreTypeConcrete::Uint128MulGuarantee(_) | CoreTypeConcrete::Coupon(_) => true, // Normal types: CoreTypeConcrete::Array(_) @@ -634,7 +636,7 @@ impl TypeBuilder for CoreTypeConcrete { CoreTypeConcrete::EcState(_) => layout_repeat(&get_integer_layout(252), 4)?.0, CoreTypeConcrete::Felt252(_) => get_integer_layout(252), CoreTypeConcrete::GasBuiltin(_) => get_integer_layout(128), - CoreTypeConcrete::BuiltinCosts(_) => Layout::new::<()>(), + CoreTypeConcrete::BuiltinCosts(_) => Layout::new::<*const ()>(), CoreTypeConcrete::Uint8(_) => get_integer_layout(8), CoreTypeConcrete::Uint16(_) => get_integer_layout(16), CoreTypeConcrete::Uint32(_) => get_integer_layout(32), @@ -691,7 +693,11 @@ impl TypeBuilder for CoreTypeConcrete { StarkNetTypeConcrete::StorageAddress(_) => get_integer_layout(252), StarkNetTypeConcrete::System(_) => Layout::new::<*mut ()>(), StarkNetTypeConcrete::Secp256Point(_) => { - get_integer_layout(256).extend(get_integer_layout(256))?.0 + get_integer_layout(256) + .extend(get_integer_layout(256))? + .0 + .extend(get_integer_layout(1))? + .0 } StarkNetTypeConcrete::Sha256StateHandle(_) => Layout::new::<*mut ()>(), }, diff --git a/src/types/builtin_costs.rs b/src/types/builtin_costs.rs index b7a202dce..7fc2e978c 100644 --- a/src/types/builtin_costs.rs +++ b/src/types/builtin_costs.rs @@ -1,4 +1,7 @@ //! # Builtin costs type +//! +//! A ptr to a list of u64, this list will not change at runtime in size and thus we only really need to store the pointer, +//! it can be allocated on the stack on rust side and passed. use super::WithSelf; use crate::{error::Result, metadata::MetadataStorage}; @@ -11,7 +14,7 @@ use cairo_lang_sierra::{ }; use melior::{ dialect::llvm, - ir::{r#type::IntegerType, Module, Type}, + ir::{Module, Type}, Context, }; @@ -25,5 +28,6 @@ pub fn build<'ctx>( _metadata: &mut MetadataStorage, _info: WithSelf, ) -> Result> { - Ok(llvm::r#type::array(IntegerType::new(context, 8).into(), 0)) + // A ptr to a list of u64 + Ok(llvm::r#type::pointer(context, 0)) } diff --git a/src/types/circuit.rs b/src/types/circuit.rs index 901a40cb3..80ed3471b 100644 --- a/src/types/circuit.rs +++ b/src/types/circuit.rs @@ -218,6 +218,7 @@ pub fn layout( | CircuitTypeConcrete::U96LimbsLessThanGuarantee(_) | CircuitTypeConcrete::Circuit(_) | CircuitTypeConcrete::CircuitDescriptor(_) + | CircuitTypeConcrete::CircuitPartialOutputs(_) | CircuitTypeConcrete::CircuitFailureGuarantee(_) => Ok(Layout::new::<()>()), CircuitTypeConcrete::CircuitData(info) => { @@ -279,8 +280,5 @@ pub fn layout( Ok(layout) } - CircuitTypeConcrete::CircuitPartialOutputs(_) => { - todo!("CircuitPartialOutputs is noop for now") - } } } diff --git a/src/types/felt252_dict.rs b/src/types/felt252_dict.rs index 768f36d51..2de679d19 100644 --- a/src/types/felt252_dict.rs +++ b/src/types/felt252_dict.rs @@ -28,7 +28,7 @@ use melior::{ dialect::{func, llvm, ods}, ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, - Block, Location, Module, Region, Type, + Attribute, Block, Identifier, Location, Module, Region, Type, }, Context, }; @@ -126,7 +126,16 @@ fn build_dup<'ctx>( false, )), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], location, )); } @@ -191,7 +200,16 @@ fn build_drop<'ctx>( false, )), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], location, )); diff --git a/src/types/felt252_dict_entry.rs b/src/types/felt252_dict_entry.rs index 23fba74f1..ba0740cf3 100644 --- a/src/types/felt252_dict_entry.rs +++ b/src/types/felt252_dict_entry.rs @@ -4,11 +4,10 @@ //! //! It is represented as the following struct: //! -//! | Index | Type | Description | -//! | ----- | -------------- | -------------------------------- | -//! | 0 | `i252` | The entry key. | -//! | 1 | `!llvm.ptr` | Pointer to the entry value. | -//! | 2 | `!llvm.ptr` | Pointer to the dictionary (rust) | +//! | Index | Type | Description | +//! | ----- | -------------- | -------------------------------------------- | +//! | 0 | `!llvm.ptr` | Pointer to the dictionary (Rust). | +//! | 1 | `!llvm.ptr` | Pointer to the entry's value pointer (Rust). | //! use super::WithSelf; @@ -22,7 +21,7 @@ use cairo_lang_sierra::{ }; use melior::{ dialect::llvm, - ir::{r#type::IntegerType, Module, Type}, + ir::{Module, Type}, Context, }; @@ -36,12 +35,12 @@ pub fn build<'ctx>( _metadata: &mut MetadataStorage, _info: WithSelf, ) -> Result> { + // Note: This is neither droppable nor cloneable. Ok(llvm::r#type::r#struct( context, &[ - IntegerType::new(context, 252).into(), // entry key - llvm::r#type::pointer(context, 0), // value ptr - llvm::r#type::pointer(context, 0), // dict ptr + llvm::r#type::pointer(context, 0), // dict ptr + llvm::r#type::pointer(context, 0), // value ptr ], false, )) diff --git a/src/types/squashed_felt252_dict.rs b/src/types/squashed_felt252_dict.rs index 0ee19a818..a54af17b2 100644 --- a/src/types/squashed_felt252_dict.rs +++ b/src/types/squashed_felt252_dict.rs @@ -10,7 +10,6 @@ use cairo_lang_sierra::{ program_registry::ProgramRegistry, }; use melior::{ - dialect::llvm, ir::{Module, Type}, Context, }; @@ -20,10 +19,10 @@ use melior::{ /// Check out [the module](self) for more info. pub fn build<'ctx>( context: &'ctx Context, - _module: &Module<'ctx>, - _registry: &ProgramRegistry, - _metadata: &mut MetadataStorage, - _info: WithSelf, + module: &Module<'ctx>, + registry: &ProgramRegistry, + metadata: &mut MetadataStorage, + info: WithSelf, ) -> Result> { - Ok(llvm::r#type::pointer(context, 0)) + super::felt252_dict::build(context, module, registry, metadata, info) } diff --git a/src/types/starknet.rs b/src/types/starknet.rs index 231b8ec5d..9299125c2 100644 --- a/src/types/starknet.rs +++ b/src/types/starknet.rs @@ -22,7 +22,14 @@ // TODO: Maybe the types used here can be i251 instead of i252. use super::WithSelf; -use crate::{error::Result, metadata::MetadataStorage}; +use crate::{ + error::Result, + metadata::{ + drop_overrides::DropOverridesMeta, dup_overrides::DupOverridesMeta, + realloc_bindings::ReallocBindingsMeta, MetadataStorage, + }, + utils::BlockExt, +}; use cairo_lang_sierra::{ extensions::{ core::{CoreLibfunc, CoreType}, @@ -32,8 +39,8 @@ use cairo_lang_sierra::{ program_registry::ProgramRegistry, }; use melior::{ - dialect::llvm, - ir::{r#type::IntegerType, Module, Type}, + dialect::{func, llvm, ods}, + ir::{attribute::IntegerAttribute, r#type::IntegerType, Block, Location, Module, Region, Type}, Context, }; @@ -180,6 +187,7 @@ pub fn build_secp256_point<'ctx>( ], false, ), + IntegerType::new(context, 1).into(), ], false, )) @@ -187,11 +195,61 @@ pub fn build_secp256_point<'ctx>( pub fn build_sha256_state_handle<'ctx>( context: &'ctx Context, - _module: &Module<'ctx>, - _registry: &ProgramRegistry, - _metadata: &mut MetadataStorage, - _info: WithSelf, + module: &Module<'ctx>, + registry: &ProgramRegistry, + metadata: &mut MetadataStorage, + info: WithSelf, ) -> Result> { + let location = Location::unknown(context); + if metadata.get::().is_none() { + metadata.insert(ReallocBindingsMeta::new(context, module)); + } + + DupOverridesMeta::register_with(context, module, registry, metadata, info.self_ty(), |_| { + let region = Region::new(); + let block = + region.append_block(Block::new(&[(llvm::r#type::pointer(context, 0), location)])); + + let null_ptr = + block.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?; + let k32 = block.const_int(context, location, 32, 64)?; + let new_ptr = block.append_op_result(ReallocBindingsMeta::realloc( + context, null_ptr, k32, location, + ))?; + + block.append_operation( + ods::llvm::intr_memcpy_inline( + context, + new_ptr, + block.argument(0)?.into(), + IntegerAttribute::new(IntegerType::new(context, 64).into(), 32), + IntegerAttribute::new(IntegerType::new(context, 1).into(), 0), + location, + ) + .into(), + ); + + block.append_operation(func::r#return( + &[block.argument(0)?.into(), new_ptr], + location, + )); + Ok(Some(region)) + })?; + DropOverridesMeta::register_with(context, module, registry, metadata, info.self_ty(), |_| { + let region = Region::new(); + let block = + region.append_block(Block::new(&[(llvm::r#type::pointer(context, 0), location)])); + + block.append_operation(ReallocBindingsMeta::free( + context, + block.argument(0)?.into(), + location, + )); + + block.append_operation(func::r#return(&[], location)); + Ok(Some(region)) + })?; + // A ptr to a heap (realloc) allocated [u32; 8] Ok(llvm::r#type::pointer(context, 0)) } diff --git a/src/utils.rs b/src/utils.rs index ee58c2ec5..9dc73cb4d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,7 +5,9 @@ pub(crate) use self::{ }; use crate::{metadata::MetadataStorage, OptLevel}; use cairo_lang_compiler::CompilerConfig; +use cairo_lang_runner::token_gas_cost; use cairo_lang_sierra::{ + extensions::gas::CostTokenType, ids::FunctionId, program::{GenFunction, Program, StatementIdx}, }; @@ -15,6 +17,7 @@ use melior::{ Context, Error, ExecutionEngine, }; use num_bigint::{BigInt, BigUint, Sign}; +use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; use std::sync::LazyLock; use std::{ @@ -23,12 +26,12 @@ use std::{ fmt::{self, Display}, ops::Neg, path::Path, - ptr::NonNull, sync::Arc, }; use thiserror::Error; mod block_ext; +pub mod mem_tracing; mod program_registry_ext; mod range_ext; @@ -49,6 +52,56 @@ pub static HALF_PRIME: LazyLock = LazyLock::new(|| { .unwrap() }); +#[derive(Debug, Clone, Copy, Deserialize, Serialize)] +pub struct BuiltinCosts { + pub r#const: u64, + pub pedersen: u64, + pub bitwise: u64, + pub ecop: u64, + pub poseidon: u64, + pub add_mod: u64, + pub mul_mod: u64, +} + +impl From for [u64; 7] { + // Order matters, for the libfunc impl + // https://github.com/starkware-libs/sequencer/blob/1b7252f8a30244d39614d7666aa113b81291808e/crates/blockifier/src/execution/entry_point_execution.rs#L208 + fn from(value: BuiltinCosts) -> Self { + [ + value.r#const, + value.pedersen, + value.bitwise, + value.ecop, + value.poseidon, + value.add_mod, + value.mul_mod, + ] + } +} + +impl Default for BuiltinCosts { + fn default() -> Self { + Self { + r#const: token_gas_cost(CostTokenType::Const) as u64, + pedersen: token_gas_cost(CostTokenType::Pedersen) as u64, + bitwise: token_gas_cost(CostTokenType::Bitwise) as u64, + ecop: token_gas_cost(CostTokenType::EcOp) as u64, + poseidon: token_gas_cost(CostTokenType::Poseidon) as u64, + add_mod: token_gas_cost(CostTokenType::AddMod) as u64, + mul_mod: token_gas_cost(CostTokenType::MulMod) as u64, + } + } +} + +#[cfg(feature = "with-mem-tracing")] +#[allow(unused_imports)] +pub(crate) use self::mem_tracing::{ + _wrapped_free as libc_free, _wrapped_malloc as libc_malloc, _wrapped_realloc as libc_realloc, +}; +#[cfg(not(feature = "with-mem-tracing"))] +#[allow(unused_imports)] +pub(crate) use libc::{free as libc_free, malloc as libc_malloc, realloc as libc_realloc}; + /// Generate a function name. /// /// If the program includes function identifiers, return those. Otherwise return `f` followed by the @@ -227,6 +280,9 @@ pub fn create_engine( .unwrap() .register_impls(&engine); + #[cfg(feature = "with-mem-tracing")] + self::mem_tracing::register_bindings(&engine); + #[cfg(feature = "with-trace-dump")] _metadata .get::() @@ -335,16 +391,6 @@ pub fn register_runtime_symbols(engine: &ExecutionEngine) { as *mut (), ); - engine.register_symbol( - "cairo_native__dict_insert", - cairo_native_runtime::cairo_native__dict_insert - as *const fn( - *mut FeltDict, - &[u8; 32], - NonNull, - ) -> *mut std::ffi::c_void as *mut (), - ); - engine.register_symbol( "cairo_native__dict_gas_refund", cairo_native_runtime::cairo_native__dict_gas_refund as *const fn(*const FeltDict) -> u64 @@ -600,8 +646,7 @@ pub mod test { .compile(program, false) .expect("Could not compile test program to MLIR."); - // FIXME: There are some bugs with non-zero LLVM optimization levels. - let executor = JitNativeExecutor::from_native_module(module, OptLevel::None); + let executor = JitNativeExecutor::from_native_module(module, OptLevel::Less); executor .invoke_dynamic_with_syscall_handler( entry_point_id, diff --git a/src/utils/mem_tracing.rs b/src/utils/mem_tracing.rs new file mode 100644 index 000000000..1a6a26b6a --- /dev/null +++ b/src/utils/mem_tracing.rs @@ -0,0 +1,131 @@ +#![cfg(feature = "with-mem-tracing")] + +use libc::{c_void, size_t}; +use melior::ExecutionEngine; +use std::cell::UnsafeCell; + +thread_local! { + static MEM_TRACING: UnsafeCell = const { UnsafeCell::new(MemTracing::new()) }; +} + +struct MemTracing { + finished: Vec, + pending: Vec, +} + +struct AllocTrace { + ptr: *mut c_void, + len: size_t, +} + +impl MemTracing { + pub const fn new() -> Self { + Self { + finished: Vec::new(), + pending: Vec::new(), + } + } + + pub fn push(&mut self, trace: AllocTrace) { + match self.pending.binary_search_by_key(&trace.ptr, |x| x.ptr) { + Ok(_) => unreachable!(), + Err(pos) => self.pending.insert(pos, trace), + } + } + + pub fn update(&mut self, ptr: *mut c_void, trace: AllocTrace) { + if let Ok(pos) = self.pending.binary_search_by_key(&ptr, |x| x.ptr) { + let trace = self.pending.remove(pos); + if trace.len == 0 { + self.finished.push(trace); + return; + } + }; + + self.push(trace); + } + + pub fn finish(&mut self, ptr: *mut c_void) { + if ptr.is_null() { + return; + } + + match self.pending.binary_search_by_key(&ptr, |x| x.ptr) { + Ok(pos) => { + let trace = self.pending.remove(pos); + self.finished.push(trace); + } + Err(_) => unreachable!(), + } + } +} + +impl AllocTrace { + pub fn new(ptr: *mut c_void, len: size_t) -> Self { + Self { ptr, len } + } +} + +pub(crate) fn register_bindings(engine: &ExecutionEngine) { + unsafe { + engine.register_symbol( + "malloc", + _wrapped_malloc as *const fn(size_t) -> *mut c_void as *mut (), + ); + engine.register_symbol( + "realloc", + _wrapped_realloc as *const fn(*mut c_void, size_t) -> *mut c_void as *mut (), + ); + engine.register_symbol("free", _wrapped_free as *const fn(*mut c_void) as *mut ()); + } +} + +pub fn report_stats() { + unsafe { + MEM_TRACING.with(|x| { + println!(); + println!("[MemTracing] Stats:"); + println!( + "[MemTracing] Freed allocations: {}", + (*x.get()).finished.len() + ); + println!("[MemTracing] Pending allocations:"); + for AllocTrace { ptr, len } in &(*x.get()).pending { + println!("[MemTracing] - {ptr:?} ({len} bytes)"); + } + + assert!((*x.get()).pending.is_empty()); + *x.get() = MemTracing::new(); + }); + } +} + +pub(crate) unsafe extern "C" fn _wrapped_malloc(len: size_t) -> *mut c_void { + let ptr = libc::malloc(len); + + println!("[MemTracing] Allocating ptr {ptr:?} with {len} bytes."); + MEM_TRACING.with(|x| (*x.get()).push(AllocTrace::new(ptr, len))); + + ptr +} + +pub(crate) unsafe extern "C" fn _wrapped_realloc(ptr: *mut c_void, len: size_t) -> *mut c_void { + let new_ptr = libc::realloc(ptr, len); + + println!("[MemTracing] Reallocating {ptr:?} into {new_ptr:?} with {len} bytes."); + MEM_TRACING.with(|x| (*x.get()).update(ptr, AllocTrace::new(new_ptr, len))); + + new_ptr +} + +pub(crate) unsafe extern "C" fn _wrapped_free(ptr: *mut c_void) { + if !ptr.is_null() { + // This print is placed before the actual call to log pointers before double free + // situations. + println!("[MemTracing] Freeing {ptr:?}."); + + libc::free(ptr); + + MEM_TRACING.with(|x| (*x.get()).finish(ptr)); + } +} diff --git a/src/values.rs b/src/values.rs index 4d41e96bb..796933dec 100644 --- a/src/values.rs +++ b/src/values.rs @@ -1,11 +1,14 @@ -//! # JIT params and return values de/serialization - -//! A Rusty interface to provide parameters to JIT calls. +//! # Params and return values de/serialization +//! +//! A Rusty interface to provide parameters to cairo-native entry point calls. use crate::{ error::{CompilerError, Error}, + starknet::{Secp256k1Point, Secp256r1Point}, types::TypeBuilder, - utils::{felt252_bigint, get_integer_layout, layout_repeat, RangeExt, PRIME}, + utils::{ + felt252_bigint, get_integer_layout, layout_repeat, libc_free, libc_malloc, RangeExt, PRIME, + }, }; use bumpalo::Bump; use cairo_lang_sierra::{ @@ -66,14 +69,8 @@ pub enum Value { Sint128(i128), EcPoint(Felt, Felt), EcState(Felt, Felt, Felt, Felt), - Secp256K1Point { - x: (u128, u128), - y: (u128, u128), - }, - Secp256R1Point { - x: (u128, u128), - y: (u128, u128), - }, + Secp256K1Point(Secp256k1Point), + Secp256R1Point(Secp256r1Point), BoundedInt { value: Felt, #[serde(with = "range_serde")] @@ -239,7 +236,10 @@ impl Value { let elem_ty = registry.get_type(&info.ty)?; let elem_layout = elem_ty.layout(registry)?.pad_to_align(); - let ptr: *mut () = libc::malloc(elem_layout.size() * data.len()).cast(); + let ptr: *mut () = match elem_layout.size() * data.len() { + 0 => std::ptr::null_mut(), + len => libc_malloc(len).cast(), + }; let len: u32 = data .len() .try_into() @@ -388,7 +388,11 @@ impl Value { let elem_ty = registry.get_type(&info.ty)?; let elem_layout = elem_ty.layout(registry)?.pad_to_align(); - let mut value_map = Box::::default(); + let mut value_map = Box::new(FeltDict { + inner: HashMap::default(), + count: 0, + free_fn: crate::utils::libc_free, + }); // next key must be called before next_value @@ -396,20 +400,14 @@ impl Value { let key = key.to_bytes_le(); let value = value.to_ptr(arena, registry, &info.ty)?; - let value_malloc_ptr = libc::malloc(elem_layout.size()); - + let value_malloc_ptr = libc_malloc(elem_layout.size()); std::ptr::copy_nonoverlapping( value.cast::().as_ptr(), value_malloc_ptr.cast(), elem_layout.size(), ); - value_map.inner.insert( - key, - NonNull::new(value_malloc_ptr) - .expect("allocation failure") - .cast(), - ); + value_map.inner.insert(key, value_malloc_ptr); } NonNull::new_unchecked(Box::into_raw(value_map)).cast() @@ -565,7 +563,7 @@ impl Value { } if !init_data_ptr.is_null() { - libc::free(init_data_ptr.cast()); + libc_free(init_data_ptr.cast()); } Self::Array(array_value) @@ -573,16 +571,24 @@ impl Value { CoreTypeConcrete::Box(info) => { let inner = *ptr.cast::>().as_ptr(); let value = Self::from_ptr(inner, &info.ty, registry)?; - libc::free(inner.as_ptr().cast()); + libc_free(inner.as_ptr().cast()); value } CoreTypeConcrete::EcPoint(_) => { - let data = ptr.cast::<[[u8; 32]; 2]>().as_ref(); + let data = ptr.cast::<[[u8; 32]; 2]>().as_mut(); + + data[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + data[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). Self::EcPoint(Felt::from_bytes_le(&data[0]), Felt::from_bytes_le(&data[1])) } CoreTypeConcrete::EcState(_) => { - let data = ptr.cast::<[[u8; 32]; 4]>().as_ref(); + let data = ptr.cast::<[[u8; 32]; 4]>().as_mut(); + + data[0][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + data[1][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + data[2][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + data[3][31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). Self::EcState( Felt::from_bytes_le(&data[0]), @@ -592,7 +598,8 @@ impl Value { ) } CoreTypeConcrete::Felt252(_) => { - let data = ptr.cast::<[u8; 32]>().as_ref(); + let data = ptr.cast::<[u8; 32]>().as_mut(); + data[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). let data = Felt::from_bytes_le_slice(data); Self::Felt252(data) } @@ -618,7 +625,7 @@ impl Value { &info.ty, registry, )?; - libc::free(inner_ptr.cast()); + libc_free(inner_ptr.cast()); value } } @@ -647,6 +654,12 @@ impl Value { }, }; + // Filter out bits that are not part of the enum's tag. + let tag_value = tag_value + & 1usize + .wrapping_shl(info.variants.len().next_power_of_two().trailing_zeros()) + .wrapping_sub(1); + let payload_ty = registry.get_type(&info.variants[tag_value])?; let payload_layout = payload_ty.layout(registry)?; @@ -697,10 +710,23 @@ impl Value { ); let mut output_map = HashMap::with_capacity(inner.len()); - for (key, val_ptr) in inner.iter() { - let key = Felt::from_bytes_le(key); - output_map.insert(key, Self::from_ptr(val_ptr.cast(), &info.ty, registry)?); - libc::free(val_ptr.as_ptr()); + for (mut key, val_ptr) in inner.into_iter() { + if val_ptr.is_null() { + continue; + } + + key[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). + + let key = Felt::from_bytes_le(&key); + output_map.insert( + key, + Self::from_ptr( + NonNull::new(val_ptr).unwrap().cast(), + &info.ty, + registry, + )?, + ); + libc_free(val_ptr); } Self::Felt252Dict { @@ -728,24 +754,24 @@ impl Value { | StarkNetTypeConcrete::StorageBaseAddress(_) | StarkNetTypeConcrete::StorageAddress(_) => { // felt values - let data = ptr.cast::<[u8; 32]>().as_ref(); + let data = ptr.cast::<[u8; 32]>().as_mut(); + data[31] &= 0x0F; // Filter out first 4 bits (they're outside an i252). let data = Felt::from_bytes_le(data); Self::Felt252(data) } StarkNetTypeConcrete::System(_) => { unreachable!("should be handled before") } - StarkNetTypeConcrete::Secp256Point(info) => { - let data = ptr.cast::<[[u128; 2]; 2]>().as_ref(); - - let x = (data[0][0], data[0][1]); - let y = (data[1][0], data[1][1]); - - match info { - Secp256PointTypeConcrete::K1(_) => Self::Secp256K1Point { x, y }, - Secp256PointTypeConcrete::R1(_) => Self::Secp256R1Point { x, y }, + StarkNetTypeConcrete::Secp256Point(info) => match info { + Secp256PointTypeConcrete::K1(_) => { + let data = ptr.cast::().as_ref(); + Self::Secp256K1Point(*data) } - } + Secp256PointTypeConcrete::R1(_) => { + let data = ptr.cast::().as_ref(); + Self::Secp256R1Point(*data) + } + }, StarkNetTypeConcrete::Sha256StateHandle(_) => todo!(), }, CoreTypeConcrete::Span(_) => todo!("implement span from_ptr"), diff --git a/tests/common.rs b/tests/common.rs index f0cac40da..9ca95d52b 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -391,7 +391,6 @@ pub fn run_vm_contract( .collect_vec() } -#[track_caller] pub fn compare_inputless_program(program_path: &str) { let program: (String, Program, SierraCasmRunner) = load_cairo_path(program_path); let program = &program; @@ -438,7 +437,6 @@ pub fn run_native_starknet_contract( /// the results automatically, triggering a proptest assert if there is a mismatch. /// /// Left of report of the assert is the cairo vm result, right side is cairo native -#[track_caller] pub fn compare_outputs( program: &Program, entry_point: &FunctionId, @@ -744,8 +742,12 @@ pub fn compare_outputs( .unwrap_or(false) }); assert_eq!( - vm_result.gas_counter.unwrap_or_else(|| Felt::from(0)), - Felt::from(native_result.remaining_gas.unwrap_or(0)), + vm_result + .gas_counter + .unwrap_or_else(|| Felt::from(0)) + .to_bigint(), + Felt::from(native_result.remaining_gas.unwrap_or(0)).to_bigint(), + "gas mismatch" ); let vm_result = match &vm_result.value { @@ -807,7 +809,11 @@ pub fn compare_outputs( }, }; - pretty_assertions_sorted::assert_eq!(native_result.return_value, vm_result); + pretty_assertions_sorted::assert_eq!( + native_result.return_value, + vm_result, + "return value mismatch" + ); Ok(()) } diff --git a/tests/tests/starknet/contracts/test_u256_order.cairo b/tests/tests/starknet/contracts/test_u256_order.cairo index 181d1242e..47a43d38c 100644 --- a/tests/tests/starknet/contracts/test_u256_order.cairo +++ b/tests/tests/starknet/contracts/test_u256_order.cairo @@ -15,12 +15,12 @@ mod Keccak { #[abi(embed_v0)] impl Keccak of super::IKeccak { fn cairo_keccak_test(self: @ContractState) -> felt252 { - let input : Array:: = array![1,2,4,5,6,6,7,2,3,4,4,5,5,6,7,7,2]; + let input: Array:: = array![1, 2, 4, 5, 6, 6, 7, 2, 3, 4, 4, 5, 5, 6, 7, 7, 2]; let output = starknet::syscalls::keccak_syscall(input.span()).unwrap(); - if output.low == 0x9293867273ef341e81577655f28aeade && output.high == 0xf70cba9bb86caa97b086fdfa3df602ed { - panic_with_felt252('arguments swapped'); - } + assert(output.low == 0xf70cba9bb86caa97b086fdfa3df602ed, 'invalid low value'); + assert(output.high == 0x9293867273ef341e81577655f28aeade, 'invalid high value'); + output.low.into() } } diff --git a/tests/tests/starknet/keccak.rs b/tests/tests/starknet/keccak.rs index 9c9830110..9422ef6fe 100644 --- a/tests/tests/starknet/keccak.rs +++ b/tests/tests/starknet/keccak.rs @@ -38,7 +38,7 @@ fn keccak_test() { assert!(!result.failure_flag); assert_eq!( result.remaining_gas, - 340282366920938463463374607431767963515 + 340282366920938463463374607431768143515 ); assert_eq!(result.return_values, vec![1.into()]); } diff --git a/tests/tests/starknet/secp256.rs b/tests/tests/starknet/secp256.rs index 423e43c28..28a8f9ce6 100644 --- a/tests/tests/starknet/secp256.rs +++ b/tests/tests/starknet/secp256.rs @@ -250,10 +250,10 @@ impl StarknetSyscallHandler for &mut SyscallHandler { fn sha256_process_block( &mut self, - _prev_state: &[u32; 8], - _current_block: &[u32; 16], + _state: &mut [u32; 8], + _block: &[u32; 16], _remaining_gas: &mut u128, - ) -> SyscallResult<[u32; 8]> { + ) -> SyscallResult<()> { unimplemented!() } } @@ -273,6 +273,7 @@ fn secp256k1_new() { Some(Secp256k1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }), Some(Secp256k1Point { x: U256 { @@ -283,6 +284,7 @@ fn secp256k1_new() { hi: u128::MAX, lo: u128::MAX, }, + is_infinity: false, }), ]), ), @@ -343,10 +345,7 @@ fn secp256k1_new() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::default())), debug_name: None, }), debug_name: None, @@ -375,10 +374,13 @@ fn secp256k1_new() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false + ))), debug_name: None, }), debug_name: None, @@ -423,6 +425,7 @@ fn secp256k1_add() { Secp256k1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }, Secp256k1Point { x: U256 { @@ -433,6 +436,7 @@ fn secp256k1_add() { lo: 0, hi: u128::MAX, }, + is_infinity: false, }, Secp256k1Point { x: U256 { @@ -443,6 +447,7 @@ fn secp256k1_add() { lo: u128::MAX, hi: u128::MAX, }, + is_infinity: false, }, ]), ), @@ -453,14 +458,8 @@ fn secp256k1_add() { &SECP256_PROGRAM, "secp256k1_add", &[ - Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }, - Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }, + Value::Secp256K1Point(Secp256k1Point::default()), + Value::Secp256K1Point(Secp256k1Point::default()), ], Some(u128::MAX), Some(&mut syscall_handler), @@ -469,10 +468,7 @@ fn secp256k1_add() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::default())), debug_name: None, }, ); @@ -481,14 +477,8 @@ fn secp256k1_add() { &SECP256_PROGRAM, "secp256k1_add", &[ - Value::Secp256K1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }, - Value::Secp256K1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }, + Value::Secp256K1Point(Secp256k1Point::new(0, u128::MAX, u128::MAX, 0, false)), + Value::Secp256K1Point(Secp256k1Point::new(u128::MAX, 0, 0, u128::MAX, false)), ], Some(u128::MAX), Some(&mut syscall_handler), @@ -497,10 +487,13 @@ fn secp256k1_add() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + 0, + 0, + u128::MAX, + false + ))), debug_name: None, }, ); @@ -509,14 +502,20 @@ fn secp256k1_add() { &SECP256_PROGRAM, "secp256k1_add", &[ - Value::Secp256K1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }, - Value::Secp256K1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }, + Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false, + )), + Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false, + )), ], Some(u128::MAX), Some(&mut syscall_handler), @@ -525,10 +524,13 @@ fn secp256k1_add() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false + ))), debug_name: None, }, ); @@ -540,10 +542,12 @@ fn secp256k1_add() { Secp256k1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }, Secp256k1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }, ), ( @@ -556,6 +560,7 @@ fn secp256k1_add() { hi: 0, lo: u128::MAX }, + is_infinity: false, }, Secp256k1Point { x: U256 { @@ -566,6 +571,7 @@ fn secp256k1_add() { hi: u128::MAX, lo: 0 }, + is_infinity: false, }, ), ( @@ -578,6 +584,7 @@ fn secp256k1_add() { lo: u128::MAX, hi: u128::MAX }, + is_infinity: false, }, Secp256k1Point { x: U256 { @@ -588,6 +595,7 @@ fn secp256k1_add() { lo: u128::MAX, hi: u128::MAX }, + is_infinity: false, }, ), ], @@ -604,6 +612,7 @@ fn secp256k1_mul() { Secp256k1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }, Secp256k1Point { x: U256 { @@ -614,6 +623,7 @@ fn secp256k1_mul() { hi: 0, lo: u128::MAX, }, + is_infinity: false, }, Secp256k1Point { x: U256 { @@ -624,6 +634,7 @@ fn secp256k1_mul() { hi: u128::MAX, lo: u128::MAX, }, + is_infinity: false, }, ]), ), @@ -634,10 +645,7 @@ fn secp256k1_mul() { &SECP256_PROGRAM, "secp256k1_mul", &[ - Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }, + Value::Secp256K1Point(Secp256k1Point::default()), Value::Struct { fields: vec![Value::Uint128(0), Value::Uint128(0)], debug_name: None, @@ -650,10 +658,7 @@ fn secp256k1_mul() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::default())), debug_name: None, }, ); @@ -662,10 +667,7 @@ fn secp256k1_mul() { &SECP256_PROGRAM, "secp256k1_mul", &[ - Value::Secp256K1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }, + Value::Secp256K1Point(Secp256k1Point::new(u128::MAX, 0, 0, u128::MAX, false)), Value::Struct { fields: vec![Value::Uint128(u128::MAX), Value::Uint128(0)], debug_name: None, @@ -678,10 +680,13 @@ fn secp256k1_mul() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::new( + 0, + u128::MAX, + u128::MAX, + 0, + false + ))), debug_name: None, }, ); @@ -690,10 +695,7 @@ fn secp256k1_mul() { &SECP256_PROGRAM, "secp256k1_mul", &[ - Value::Secp256K1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }, + Value::Secp256K1Point(Secp256k1Point::new(u128::MAX, 0, 0, u128::MAX, false)), Value::Struct { fields: vec![Value::Uint128(u128::MAX), Value::Uint128(0)], debug_name: None, @@ -706,10 +708,13 @@ fn secp256k1_mul() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false + ))), debug_name: None, }, ); @@ -717,40 +722,16 @@ fn secp256k1_mul() { assert_eq!( syscall_handler.secp256k1_mul.0, [ + (Secp256k1Point::default(), U256 { hi: 0, lo: 0 },), ( - Secp256k1Point { - x: U256 { hi: 0, lo: 0 }, - y: U256 { hi: 0, lo: 0 }, - }, - U256 { hi: 0, lo: 0 }, - ), - ( - Secp256k1Point { - x: U256 { - lo: u128::MAX, - hi: 0 - }, - y: U256 { - lo: 0, - hi: u128::MAX, - }, - }, + Secp256k1Point::new(u128::MAX, 0, 0, u128::MAX, false), U256 { lo: u128::MAX, hi: 0, }, ), ( - Secp256k1Point { - x: U256 { - hi: 0, - lo: u128::MAX, - }, - y: U256 { - hi: u128::MAX, - lo: 0, - }, - }, + Secp256k1Point::new(u128::MAX, 0, 0, u128::MAX, false), U256 { hi: 0, lo: u128::MAX, @@ -771,6 +752,7 @@ fn secp256k1_get_point_from_x() { Some(Secp256k1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }), Some(Secp256k1Point { x: U256 { @@ -781,6 +763,7 @@ fn secp256k1_get_point_from_x() { hi: u128::MAX, lo: 0, }, + is_infinity: false, }), Some(Secp256k1Point { x: U256 { @@ -791,6 +774,7 @@ fn secp256k1_get_point_from_x() { hi: 0, lo: u128::MAX, }, + is_infinity: false, }), ]), ), @@ -859,10 +843,7 @@ fn secp256k1_get_point_from_x() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::default())), debug_name: None, }), debug_name: None, @@ -895,10 +876,13 @@ fn secp256k1_get_point_from_x() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + 0, + 0, + u128::MAX, + false + ))), debug_name: None, }), debug_name: None, @@ -931,10 +915,13 @@ fn secp256k1_get_point_from_x() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256K1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }), + value: Box::new(Value::Secp256K1Point(Secp256k1Point::new( + 0, + u128::MAX, + u128::MAX, + 0, + false + ))), debug_name: None, }), debug_name: None, @@ -1016,10 +1003,7 @@ fn secp256k1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256k1_get_xy", - &[Value::Secp256K1Point { - x: (0, 0), - y: (0, 0), - }], + &[Value::Secp256K1Point(Secp256k1Point::default())], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -1047,10 +1031,13 @@ fn secp256k1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256k1_get_xy", - &[Value::Secp256K1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }], + &[Value::Secp256K1Point(Secp256k1Point::new( + 0, + u128::MAX, + u128::MAX, + 0, + false, + ))], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -1078,10 +1065,13 @@ fn secp256k1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256k1_get_xy", - &[Value::Secp256K1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }], + &[Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + 0, + 0, + u128::MAX, + false, + ))], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -1109,10 +1099,13 @@ fn secp256k1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256k1_get_xy", - &[Value::Secp256K1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }], + &[Value::Secp256K1Point(Secp256k1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false, + ))], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -1143,6 +1136,7 @@ fn secp256k1_get_xy() { Secp256k1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false }, Secp256k1Point { x: U256 { @@ -1153,6 +1147,7 @@ fn secp256k1_get_xy() { lo: u128::MAX, hi: 0, }, + is_infinity: false }, Secp256k1Point { x: U256 { @@ -1163,6 +1158,7 @@ fn secp256k1_get_xy() { lo: 0, hi: u128::MAX, }, + is_infinity: false }, Secp256k1Point { x: U256 { @@ -1173,6 +1169,7 @@ fn secp256k1_get_xy() { hi: u128::MAX, lo: u128::MAX, }, + is_infinity: false }, ], ); @@ -1189,6 +1186,7 @@ fn secp256r1_new() { Some(Secp256r1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }), Some(Secp256r1Point { x: U256 { @@ -1199,6 +1197,7 @@ fn secp256r1_new() { hi: u128::MAX, lo: u128::MAX, }, + is_infinity: false, }), ]), ), @@ -1259,10 +1258,7 @@ fn secp256r1_new() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::default())), debug_name: None, }), debug_name: None, @@ -1291,10 +1287,13 @@ fn secp256r1_new() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false + ))), debug_name: None, }), debug_name: None, @@ -1339,6 +1338,7 @@ fn secp256r1_add() { Secp256r1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }, Secp256r1Point { x: U256 { @@ -1349,6 +1349,7 @@ fn secp256r1_add() { hi: 0, lo: u128::MAX, }, + is_infinity: false, }, Secp256r1Point { x: U256 { @@ -1359,6 +1360,7 @@ fn secp256r1_add() { hi: u128::MAX, lo: u128::MAX, }, + is_infinity: false, }, ]), ), @@ -1369,14 +1371,8 @@ fn secp256r1_add() { &SECP256_PROGRAM, "secp256r1_add", &[ - Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }, - Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }, + Value::Secp256R1Point(Secp256r1Point::default()), + Value::Secp256R1Point(Secp256r1Point::default()), ], Some(u128::MAX), Some(&mut syscall_handler), @@ -1385,10 +1381,7 @@ fn secp256r1_add() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::default())), debug_name: None, }, ); @@ -1397,14 +1390,8 @@ fn secp256r1_add() { &SECP256_PROGRAM, "secp256r1_add", &[ - Value::Secp256R1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }, - Value::Secp256R1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }, + Value::Secp256R1Point(Secp256r1Point::new(u128::MAX, 0, 0, u128::MAX, false)), + Value::Secp256R1Point(Secp256r1Point::new(0, u128::MAX, u128::MAX, 0, false)), ], Some(u128::MAX), Some(&mut syscall_handler), @@ -1413,10 +1400,13 @@ fn secp256r1_add() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::new( + 0, + u128::MAX, + u128::MAX, + 0, + false + ))), debug_name: None, }, ); @@ -1425,14 +1415,20 @@ fn secp256r1_add() { &SECP256_PROGRAM, "secp256r1_add", &[ - Value::Secp256R1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }, - Value::Secp256R1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }, + Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false, + )), + Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false, + )), ], Some(u128::MAX), Some(&mut syscall_handler), @@ -1441,10 +1437,13 @@ fn secp256r1_add() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false + )),), debug_name: None, }, ); @@ -1452,59 +1451,14 @@ fn secp256r1_add() { assert_eq!( syscall_handler.secp256r1_add.0, [ + (Secp256r1Point::default(), Secp256r1Point::default(),), ( - Secp256r1Point { - x: U256 { hi: 0, lo: 0 }, - y: U256 { hi: 0, lo: 0 }, - }, - Secp256r1Point { - x: U256 { hi: 0, lo: 0 }, - y: U256 { hi: 0, lo: 0 }, - }, + Secp256r1Point::new(u128::MAX, 0, 0, u128::MAX, false), + Secp256r1Point::new(0, u128::MAX, u128::MAX, 0, false), ), ( - Secp256r1Point { - x: U256 { - lo: u128::MAX, - hi: 0 - }, - y: U256 { - lo: 0, - hi: u128::MAX - }, - }, - Secp256r1Point { - x: U256 { - lo: 0, - hi: u128::MAX - }, - y: U256 { - lo: u128::MAX, - hi: 0 - }, - }, - ), - ( - Secp256r1Point { - x: U256 { - hi: u128::MAX, - lo: u128::MAX - }, - y: U256 { - hi: u128::MAX, - lo: u128::MAX - }, - }, - Secp256r1Point { - x: U256 { - hi: u128::MAX, - lo: u128::MAX - }, - y: U256 { - hi: u128::MAX, - lo: u128::MAX - }, - }, + Secp256r1Point::new(u128::MAX, u128::MAX, u128::MAX, u128::MAX, false), + Secp256r1Point::new(u128::MAX, u128::MAX, u128::MAX, u128::MAX, false), ), ], ); @@ -1520,6 +1474,7 @@ fn secp256r1_mul() { Secp256r1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }, Secp256r1Point { x: U256 { @@ -1530,6 +1485,7 @@ fn secp256r1_mul() { hi: 0, lo: u128::MAX, }, + is_infinity: false, }, Secp256r1Point { x: U256 { @@ -1540,6 +1496,7 @@ fn secp256r1_mul() { hi: u128::MAX, lo: u128::MAX, }, + is_infinity: false, }, ]), ), @@ -1550,10 +1507,7 @@ fn secp256r1_mul() { &SECP256_PROGRAM, "secp256r1_mul", &[ - Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }, + Value::Secp256R1Point(Secp256r1Point::default()), Value::Struct { fields: vec![Value::Uint128(0), Value::Uint128(0)], debug_name: None, @@ -1566,10 +1520,7 @@ fn secp256r1_mul() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::default())), debug_name: None, }, ); @@ -1578,10 +1529,7 @@ fn secp256r1_mul() { &SECP256_PROGRAM, "secp256r1_mul", &[ - Value::Secp256R1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }, + Value::Secp256R1Point(Secp256r1Point::new(u128::MAX, 0, 0, u128::MAX, false)), Value::Struct { fields: vec![Value::Uint128(u128::MAX), Value::Uint128(0)], debug_name: None, @@ -1594,10 +1542,13 @@ fn secp256r1_mul() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::new( + 0, + u128::MAX, + u128::MAX, + 0, + false + ))), debug_name: None, }, ); @@ -1606,10 +1557,7 @@ fn secp256r1_mul() { &SECP256_PROGRAM, "secp256r1_mul", &[ - Value::Secp256R1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }, + Value::Secp256R1Point(Secp256r1Point::new(0, u128::MAX, u128::MAX, 0, false)), Value::Struct { fields: vec![Value::Uint128(0), Value::Uint128(u128::MAX)], debug_name: None, @@ -1622,10 +1570,13 @@ fn secp256r1_mul() { result.return_value, Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false + ))), debug_name: None, }, ); @@ -1633,40 +1584,16 @@ fn secp256r1_mul() { assert_eq!( syscall_handler.secp256r1_mul.0, [ + (Secp256r1Point::default(), U256 { hi: 0, lo: 0 },), ( - Secp256r1Point { - x: U256 { hi: 0, lo: 0 }, - y: U256 { hi: 0, lo: 0 }, - }, - U256 { hi: 0, lo: 0 }, - ), - ( - Secp256r1Point { - x: U256 { - lo: u128::MAX, - hi: 0 - }, - y: U256 { - lo: 0, - hi: u128::MAX, - }, - }, + Secp256r1Point::new(u128::MAX, 0, 0, u128::MAX, false), U256 { lo: u128::MAX, hi: 0, }, ), ( - Secp256r1Point { - x: U256 { - lo: 0, - hi: u128::MAX, - }, - y: U256 { - lo: u128::MAX, - hi: 0, - }, - }, + Secp256r1Point::new(0, u128::MAX, u128::MAX, 0, false), U256 { lo: 0, hi: u128::MAX, @@ -1687,6 +1614,7 @@ fn secp256r1_get_point_from_x() { Some(Secp256r1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false, }), Some(Secp256r1Point { x: U256 { @@ -1697,6 +1625,7 @@ fn secp256r1_get_point_from_x() { hi: u128::MAX, lo: 0, }, + is_infinity: false, }), Some(Secp256r1Point { x: U256 { @@ -1707,6 +1636,7 @@ fn secp256r1_get_point_from_x() { hi: 0, lo: u128::MAX, }, + is_infinity: false, }), ]), ), @@ -1775,10 +1705,7 @@ fn secp256r1_get_point_from_x() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::default())), debug_name: None, }), debug_name: None, @@ -1811,10 +1738,13 @@ fn secp256r1_get_point_from_x() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + 0, + 0, + u128::MAX, + false + ))), debug_name: None, }), debug_name: None, @@ -1847,10 +1777,13 @@ fn secp256r1_get_point_from_x() { tag: 0, value: Box::new(Value::Enum { tag: 0, - value: Box::new(Value::Secp256R1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }), + value: Box::new(Value::Secp256R1Point(Secp256r1Point::new( + 0, + u128::MAX, + u128::MAX, + 0, + false + ))), debug_name: None, }), debug_name: None, @@ -1932,10 +1865,7 @@ fn secp256r1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256r1_get_xy", - &[Value::Secp256R1Point { - x: (0, 0), - y: (0, 0), - }], + &[Value::Secp256R1Point(Secp256r1Point::default())], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -1963,10 +1893,13 @@ fn secp256r1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256r1_get_xy", - &[Value::Secp256R1Point { - x: (0, u128::MAX), - y: (u128::MAX, 0), - }], + &[Value::Secp256R1Point(Secp256r1Point::new( + 0, + u128::MAX, + u128::MAX, + 0, + false, + ))], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -1994,10 +1927,13 @@ fn secp256r1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256r1_get_xy", - &[Value::Secp256R1Point { - x: (u128::MAX, 0), - y: (0, u128::MAX), - }], + &[Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + 0, + 0, + u128::MAX, + false, + ))], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -2025,10 +1961,13 @@ fn secp256r1_get_xy() { let result = run_native_program( &SECP256_PROGRAM, "secp256r1_get_xy", - &[Value::Secp256R1Point { - x: (u128::MAX, u128::MAX), - y: (u128::MAX, u128::MAX), - }], + &[Value::Secp256R1Point(Secp256r1Point::new( + u128::MAX, + u128::MAX, + u128::MAX, + u128::MAX, + false, + ))], Some(u128::MAX), Some(&mut syscall_handler), ); @@ -2059,6 +1998,7 @@ fn secp256r1_get_xy() { Secp256r1Point { x: U256 { hi: 0, lo: 0 }, y: U256 { hi: 0, lo: 0 }, + is_infinity: false }, Secp256r1Point { x: U256 { @@ -2069,6 +2009,7 @@ fn secp256r1_get_xy() { lo: u128::MAX, hi: 0, }, + is_infinity: false }, Secp256r1Point { x: U256 { @@ -2079,6 +2020,7 @@ fn secp256r1_get_xy() { lo: 0, hi: u128::MAX, }, + is_infinity: false }, Secp256r1Point { x: U256 { @@ -2089,6 +2031,7 @@ fn secp256r1_get_xy() { hi: u128::MAX, lo: u128::MAX, }, + is_infinity: false }, ], ); diff --git a/tests/tests/starknet/syscalls.rs b/tests/tests/starknet/syscalls.rs index 8f88cd0eb..fc52ee5f0 100644 --- a/tests/tests/starknet/syscalls.rs +++ b/tests/tests/starknet/syscalls.rs @@ -496,11 +496,11 @@ impl StarknetSyscallHandler for SyscallHandler { fn sha256_process_block( &mut self, - prev_state: &[u32; 8], - _current_block: &[u32; 16], + _state: &mut [u32; 8], + _block: &[u32; 16], _remaining_gas: &mut u128, - ) -> SyscallResult<[u32; 8]> { - Ok(*prev_state) + ) -> SyscallResult<()> { + Ok(()) } } diff --git a/tests/tests/starknet/u256.rs b/tests/tests/starknet/u256.rs index 813e920e6..4ac77b790 100644 --- a/tests/tests/starknet/u256.rs +++ b/tests/tests/starknet/u256.rs @@ -43,6 +43,6 @@ fn u256_test() { ); assert_eq!( result.remaining_gas, - 340282366920938463463374607431768012315 + 340282366920938463463374607431768192415 ); } From 11c3714145c3f021b404c72d317c5544378cd5e6 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Oct 2024 10:06:38 -0300 Subject: [PATCH 10/17] Cargo.lock --- Cargo.lock | 48 ++++++------------------------------------------ 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7aba5bd07..28297e30b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,10 +300,8 @@ dependencies = [ [[package]] name = "bindgen" version = "0.69.5" -version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", @@ -953,7 +951,6 @@ dependencies = [ [[package]] name = "cairo-native" version = "0.2.0-alpha.4" -version = "0.2.0-alpha.4" dependencies = [ "anyhow", "aquamarine", @@ -961,10 +958,6 @@ dependencies = [ "ark-ff", "ark-secp256k1", "ark-secp256r1", - "ark-ec", - "ark-ff", - "ark-secp256k1", - "ark-secp256r1", "bumpalo", "cairo-lang-compiler", "cairo-lang-defs", @@ -997,7 +990,6 @@ dependencies = [ "mlir-sys", "num-bigint", "num-integer", - "num-integer", "num-traits 0.2.19", "pretty_assertions_sorted", "proptest", @@ -1021,7 +1013,6 @@ dependencies = [ [[package]] name = "cairo-native-runtime" version = "0.2.0-alpha.4" -version = "0.2.0-alpha.4" dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-gas", @@ -1099,9 +1090,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.30" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", @@ -1174,10 +1165,8 @@ dependencies = [ [[package]] name = "clap" version = "4.5.20" -version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -1186,10 +1175,8 @@ dependencies = [ [[package]] name = "clap_builder" version = "4.5.20" -version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -1510,10 +1497,8 @@ dependencies = [ [[package]] name = "derive_builder" version = "0.20.2" -version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] @@ -1521,10 +1506,8 @@ dependencies = [ [[package]] name = "derive_builder_core" version = "0.20.2" -version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", @@ -1535,10 +1518,8 @@ dependencies = [ [[package]] name = "derive_builder_macro" version = "0.20.2" -version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn 2.0.85", @@ -2236,10 +2217,8 @@ dependencies = [ [[package]] name = "js-sys" version = "0.3.72" -version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -2942,9 +2921,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -3241,10 +3220,8 @@ dependencies = [ [[package]] name = "rustversion" version = "1.0.18" -version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -3390,9 +3367,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.129" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbcf9b78a125ee667ae19388837dd12294b858d101fdd393cb9d5501ef09eb2" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -3731,7 +3708,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ecbc9175dd38627cd01d546e7b41c9a115e5773f4c98f64e2185c81ec5f45ab" dependencies = [ - "bindgen 0.69.5", "bindgen 0.69.5", "cc", "paste", @@ -4146,10 +4122,8 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.95" -version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -4159,10 +4133,8 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" version = "0.2.95" -version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -4176,10 +4148,8 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" version = "0.2.95" -version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4188,10 +4158,8 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" version = "0.2.95" -version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -4203,18 +4171,14 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" version = "0.2.95" -version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" version = "0.3.72" -version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", From 339534f50d70d15fd40ed0f11ef1e64ef4d09748 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Oct 2024 13:28:19 -0300 Subject: [PATCH 11/17] enum 0 tag --- runtime/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index af74f3dd1..0043dedf8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -674,12 +674,7 @@ pub mod trace_dump { }; use starknet_types_core::felt::Felt; use std::{ - alloc::Layout, - collections::HashMap, - mem::swap, - ops::Range, - ptr::NonNull, - sync::{LazyLock, Mutex}, + alloc::Layout, collections::HashMap, marker::PhantomData, mem::swap, ops::Range, ptr::NonNull, sync::{LazyLock, Mutex} }; use crate::FeltDict; @@ -906,7 +901,9 @@ pub mod trace_dump { CoreTypeConcrete::Enum(info) => { let tag_bits = info.variants.len().next_power_of_two().trailing_zeros(); let (tag_value, layout) = match tag_bits { - 0 => todo!(), + 0 => { + (0, Layout::new::>()) + }, width if width <= 8 => { (value_ptr.cast::().read() as usize, Layout::new::()) } From da47a0fbafbab6b53725a582fef6173f342417e2 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Oct 2024 10:37:53 -0300 Subject: [PATCH 12/17] revert builtin costs --- Cargo.lock | 32 ++++---- programs/benches/factorial_2M.c | 6 -- programs/benches/fib_2M.c | 6 -- programs/benches/logistic_map.c | 6 -- runtime/src/lib.rs | 12 ++- src/compiler.rs | 28 +------ src/error.rs | 3 - src/executor.rs | 69 +++++----------- src/executor/aot.rs | 12 --- src/executor/contract.rs | 77 +++--------------- src/executor/jit.rs | 13 --- src/libfuncs/gas.rs | 136 ++++---------------------------- src/metadata/gas.rs | 15 ++-- src/types.rs | 9 +-- src/types/builtin_costs.rs | 8 +- src/utils.rs | 44 ----------- src/values.rs | 4 +- tests/common.rs | 16 ++-- 18 files changed, 93 insertions(+), 403 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28297e30b..e3422926a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,9 +54,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -69,36 +69,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1204,9 +1204,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" @@ -3056,9 +3056,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", diff --git a/programs/benches/factorial_2M.c b/programs/benches/factorial_2M.c index 9beefef5f..48f00bd16 100644 --- a/programs/benches/factorial_2M.c +++ b/programs/benches/factorial_2M.c @@ -16,8 +16,6 @@ typedef struct factorial_return_values } result; } factorial_return_values_t; -extern uint64_t* builtin_costs; - static void run_bench(factorial_return_values_t*, uint64_t) __attribute__((weakref("_mlir_ciface_factorial_2M::factorial_2M::main(f1)"))); @@ -26,10 +24,6 @@ int main() { factorial_return_values_t return_values; - uint64_t BuiltinCosts[7] = {1, 4050, 583, 4085, 491, 230, 604}; - - builtin_costs = &BuiltinCosts[0]; - run_bench(&return_values, 0); assert(return_values.result.discriminant == 0); diff --git a/programs/benches/fib_2M.c b/programs/benches/fib_2M.c index 72a420a86..5534fb4b3 100644 --- a/programs/benches/fib_2M.c +++ b/programs/benches/fib_2M.c @@ -16,18 +16,12 @@ typedef struct fib_return_values } result; } fib_return_values_t; -extern uint64_t* builtin_costs; - static void run_bench(fib_return_values_t *, uint64_t) __attribute__((weakref("_mlir_ciface_fib_2M::fib_2M::main(f1)"))); int main() { - uint64_t BuiltinCosts[7] = {1, 4050, 583, 4085, 491, 230, 604}; - - builtin_costs = &BuiltinCosts[0]; - fib_return_values_t return_values; run_bench(&return_values, 0); diff --git a/programs/benches/logistic_map.c b/programs/benches/logistic_map.c index 483def4c4..e48008bea 100644 --- a/programs/benches/logistic_map.c +++ b/programs/benches/logistic_map.c @@ -16,18 +16,12 @@ typedef struct map_return_values } result; } map_return_values_t; -extern uint64_t* builtin_costs; - static void run_bench(map_return_values_t *, uint64_t) __attribute__((weakref("_mlir_ciface_logistic_map::logistic_map::main(f2)"))); int main() { - uint64_t BuiltinCosts[7] = {1, 4050, 583, 4085, 491, 230, 604}; - - builtin_costs = &BuiltinCosts[0]; - map_return_values_t return_values; run_bench(&return_values, 0); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0043dedf8..1e16a3726 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -674,7 +674,13 @@ pub mod trace_dump { }; use starknet_types_core::felt::Felt; use std::{ - alloc::Layout, collections::HashMap, marker::PhantomData, mem::swap, ops::Range, ptr::NonNull, sync::{LazyLock, Mutex} + alloc::Layout, + collections::HashMap, + marker::PhantomData, + mem::swap, + ops::Range, + ptr::NonNull, + sync::{LazyLock, Mutex}, }; use crate::FeltDict; @@ -901,9 +907,7 @@ pub mod trace_dump { CoreTypeConcrete::Enum(info) => { let tag_bits = info.variants.len().next_power_of_two().trailing_zeros(); let (tag_value, layout) = match tag_bits { - 0 => { - (0, Layout::new::>()) - }, + 0 => (0, Layout::new::<()>()), width if width <= 8 => { (value_ptr.cast::().read() as usize, Layout::new::()) } diff --git a/src/compiler.rs b/src/compiler.rs index 125252f08..338dd659e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -74,7 +74,7 @@ use melior::{ arith::CmpiPredicate, cf, func, index, llvm::{self, LoadStoreOptions}, - memref, ods, + memref, }, ir::{ attribute::{ @@ -136,30 +136,6 @@ pub fn compile( } } - { - // Add the builtin_costs global. - // We always add it because symbol look up otherwise can panic. - let region = Region::new(); - let location = Location::unknown(context); - let block = region.append_block(Block::new(&[])); - let value = block.append_op_result( - ods::llvm::mlir_zero(context, llvm::r#type::pointer(context, 0), location).into(), - )?; - block.append_operation(melior::dialect::llvm::r#return(Some(value), location)); - - module.body().append_operation( - ods::llvm::mlir_global( - context, - region, - TypeAttribute::new(llvm::r#type::pointer(context, 0)), - StringAttribute::new(context, "builtin_costs"), - Attribute::parse(context, "#llvm.linkage").unwrap(), - location, - ) - .into(), - ); - } - // Sierra programs have the following structure: // 1. Type declarations, one per line. // 2. Libfunc declarations, one per line. @@ -476,7 +452,7 @@ fn compile_func( initial_state, |statement_idx, mut state| { if let Some(gas_metadata) = metadata.get::() { - let gas_cost = gas_metadata.get_gas_costs_for_statement(statement_idx); + let gas_cost = gas_metadata.get_gas_cost_for_statement(statement_idx); metadata.remove::(); metadata.insert(GasCost(gas_cost)); } diff --git a/src/error.rs b/src/error.rs index 2cc503d07..c58b2fa57 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,9 +69,6 @@ pub enum Error { #[error("integer conversion failed")] IntegerConversion, - #[error("missing BuiltinCosts global symbol, should never happen, this is a bug")] - MissingBuiltinCostsSymbol, - #[error(transparent)] IoError(#[from] std::io::Error), diff --git a/src/executor.rs b/src/executor.rs index f88e87735..13a570ac6 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -10,7 +10,7 @@ use crate::{ execution_result::{BuiltinStats, ExecutionResult}, starknet::{handler::StarknetSyscallHandlerCallbacks, StarknetSyscallHandler}, types::TypeBuilder, - utils::{libc_free, BuiltinCosts, RangeExt}, + utils::{libc_free, RangeExt}, values::Value, }; use bumpalo::Bump; @@ -69,7 +69,6 @@ extern "C" { fn invoke_dynamic( registry: &ProgramRegistry, function_ptr: *const c_void, - builtin_costs_ptr: Option<*mut c_void>, function_signature: &FunctionSignature, args: &[Value], gas: u128, @@ -142,15 +141,6 @@ fn invoke_dynamic( previous_syscall_handler }); - // Order matters, for the libfunc impl - let builtin_costs: [u64; 7] = BuiltinCosts::default().into(); - - if let Some(builtin_costs_ptr) = builtin_costs_ptr { - unsafe { - *builtin_costs_ptr.cast() = builtin_costs.as_ptr(); - } - } - // Generate argument list. let mut iter = args.iter(); for item in function_signature.param_types.iter().filter_map(|type_id| { @@ -176,13 +166,6 @@ fn invoke_dynamic( (syscall_handler as *mut StarknetSyscallHandlerCallbacks<_>) .to_bytes(&mut invoke_data)?; } - CoreTypeConcrete::BuiltinCosts(_) => { - if let Some(builtin_costs_ptr) = builtin_costs_ptr { - builtin_costs_ptr.to_bytes(&mut invoke_data)?; - } else { - (builtin_costs.as_ptr()).to_bytes(&mut invoke_data)?; - } - } type_info if type_info.is_builtin() => 0u64.to_bytes(&mut invoke_data)?, type_info => ValueWithInfoWrapper { value: iter.next().unwrap(), @@ -266,38 +249,26 @@ fn invoke_dynamic( }, _ if type_info.is_builtin() => { if !type_info.is_zst(registry)? { - if let CoreTypeConcrete::BuiltinCosts(_) = type_info { - // todo: should we use this value? - let _value = match &mut return_ptr { - Some(return_ptr) => unsafe { *read_value::<*mut u64>(return_ptr) }, - None => ret_registers[0] as *mut u64, - }; - } else { - let value = match &mut return_ptr { - Some(return_ptr) => unsafe { *read_value::(return_ptr) }, - None => ret_registers[0], - } as usize; - - match type_info { - CoreTypeConcrete::Bitwise(_) => builtin_stats.bitwise = value, - CoreTypeConcrete::EcOp(_) => builtin_stats.ec_op = value, - CoreTypeConcrete::RangeCheck(_) => builtin_stats.range_check = value, - CoreTypeConcrete::Pedersen(_) => builtin_stats.pedersen = value, - CoreTypeConcrete::Poseidon(_) => builtin_stats.poseidon = value, - CoreTypeConcrete::SegmentArena(_) => { - builtin_stats.segment_arena = value - } - CoreTypeConcrete::RangeCheck96(_) => { - builtin_stats.range_check_96 = value - } - CoreTypeConcrete::Circuit(CircuitTypeConcrete::AddMod(_)) => { - builtin_stats.circuit_add = value - } - CoreTypeConcrete::Circuit(CircuitTypeConcrete::MulMod(_)) => { - builtin_stats.circuit_mul = value - } - _ => unreachable!("{type_id:?}"), + let value = match &mut return_ptr { + Some(return_ptr) => unsafe { *read_value::(return_ptr) }, + None => ret_registers[0], + } as usize; + + match type_info { + CoreTypeConcrete::Bitwise(_) => builtin_stats.bitwise = value, + CoreTypeConcrete::EcOp(_) => builtin_stats.ec_op = value, + CoreTypeConcrete::RangeCheck(_) => builtin_stats.range_check = value, + CoreTypeConcrete::Pedersen(_) => builtin_stats.pedersen = value, + CoreTypeConcrete::Poseidon(_) => builtin_stats.poseidon = value, + CoreTypeConcrete::SegmentArena(_) => builtin_stats.segment_arena = value, + CoreTypeConcrete::RangeCheck96(_) => builtin_stats.range_check_96 = value, + CoreTypeConcrete::Circuit(CircuitTypeConcrete::AddMod(_)) => { + builtin_stats.circuit_add = value + } + CoreTypeConcrete::Circuit(CircuitTypeConcrete::MulMod(_)) => { + builtin_stats.circuit_mul = value } + _ => unreachable!("{type_id:?}"), } } } diff --git a/src/executor/aot.rs b/src/executor/aot.rs index bff56301c..31a05bc30 100644 --- a/src/executor/aot.rs +++ b/src/executor/aot.rs @@ -81,7 +81,6 @@ impl AotNativeExecutor { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), - self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id), args, available_gas, @@ -104,7 +103,6 @@ impl AotNativeExecutor { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), - self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id), args, available_gas, @@ -127,7 +125,6 @@ impl AotNativeExecutor { ContractExecutionResult::from_execution_result(super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), - self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id), &[Value::Struct { fields: vec![Value::Array( @@ -155,15 +152,6 @@ impl AotNativeExecutor { } } - pub fn find_symbol_ptr(&self, name: &str) -> Option<*mut c_void> { - unsafe { - self.library - .get::<*mut ()>(name.as_bytes()) - .ok() - .map(|x| x.into_raw().into_raw()) - } - } - fn extract_signature(&self, function_id: &FunctionId) -> &FunctionSignature { &self.registry.get_function(function_id).unwrap().signature } diff --git a/src/executor/contract.rs b/src/executor/contract.rs index e6bea6ff2..fcdd9c233 100644 --- a/src/executor/contract.rs +++ b/src/executor/contract.rs @@ -34,7 +34,7 @@ use crate::{ arch::AbiArgument, context::NativeContext, - error::{Error, Result}, + error::Result, execution_result::{BuiltinStats, ContractExecutionResult}, executor::invoke_trampoline, module::NativeModule, @@ -42,7 +42,6 @@ use crate::{ types::TypeBuilder, utils::{ decode_error_message, generate_function_name, get_integer_layout, libc_free, libc_malloc, - BuiltinCosts, }, OptLevel, }; @@ -77,23 +76,12 @@ pub struct AotContractExecutor { pub library: Arc, path: PathBuf, is_temp_path: bool, - contract_info: ContractInfo, + entry_points_info: BTreeMap, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub struct ContractInfo { - pub version: ContractInfoVersion, - pub entry_points: BTreeMap, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub enum ContractInfoVersion { - Version0, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub struct EntryPointInfo { - pub builtins: Vec, +struct EntryPointInfo { + builtins: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] @@ -109,7 +97,6 @@ pub enum BuiltinType { CircuitMul, Gas, System, - BuiltinCosts, } impl AotContractExecutor { @@ -147,9 +134,6 @@ impl AotContractExecutor { CoreTypeConcrete::RangeCheck(_) => builtins.push(BuiltinType::RangeCheck), CoreTypeConcrete::Pedersen(_) => builtins.push(BuiltinType::Pedersen), CoreTypeConcrete::Poseidon(_) => builtins.push(BuiltinType::Poseidon), - CoreTypeConcrete::BuiltinCosts(_) => { - builtins.push(BuiltinType::BuiltinCosts) - } CoreTypeConcrete::SegmentArena(_) => { builtins.push(BuiltinType::SegmentArena) } @@ -188,10 +172,7 @@ impl AotContractExecutor { library: Arc::new(unsafe { Library::new(&library_path)? }), path: library_path, is_temp_path: true, - contract_info: ContractInfo { - version: ContractInfoVersion::Version0, - entry_points: infos, - }, + entry_points_info: infos, }) } @@ -200,9 +181,9 @@ impl AotContractExecutor { let to = to.as_ref(); std::fs::copy(&self.path, to)?; - let contract_info = serde_json::to_string(&self.contract_info)?; + let info = serde_json::to_string(&self.entry_points_info)?; let path = to.with_extension("json"); - std::fs::write(path, contract_info)?; + std::fs::write(path, info)?; self.path = to.to_path_buf(); self.is_temp_path = false; @@ -213,12 +194,12 @@ impl AotContractExecutor { /// Load the executor from an already compiled library with the additional info json file. pub fn load(library_path: &Path) -> Result { let info_str = std::fs::read_to_string(library_path.with_extension("json"))?; - let contract_info: ContractInfo = serde_json::from_str(&info_str)?; + let info: BTreeMap = serde_json::from_str(&info_str)?; Ok(Self { library: Arc::new(unsafe { Library::new(library_path)? }), path: library_path.to_path_buf(), is_temp_path: false, - contract_info, + entry_points_info: info, }) } @@ -228,30 +209,16 @@ impl AotContractExecutor { function_id: &FunctionId, args: &[Felt], gas: Option, - builtin_costs: Option, mut syscall_handler: impl StarknetSyscallHandler, ) -> Result { let arena = Bump::new(); let mut invoke_data = Vec::::new(); let function_ptr = self.find_function_ptr(function_id, true)?; - let builtin_costs_ptr = self - .find_symbol_ptr("builtin_costs") - .ok_or_else(|| Error::MissingBuiltinCostsSymbol)?; - - let builtin_costs = builtin_costs.unwrap_or_default(); - let builtin_costs: [u64; 7] = builtin_costs.into(); - - unsafe { - *builtin_costs_ptr.cast() = builtin_costs.as_ptr(); - } // it can vary from contract to contract thats why we need to store/ load it. // substract 2, which are the gas and syscall builtin - let num_builtins = self.contract_info.entry_points[&function_id.id] - .builtins - .len() - - 2; + let num_builtins = self.entry_points_info[&function_id.id].builtins.len() - 2; // There is always a return ptr because contracts always return more than 1 thing (builtin counters, syscall, enum) let return_ptr = arena.alloc_layout(unsafe { @@ -264,15 +231,12 @@ impl AotContractExecutor { let mut syscall_handler = StarknetSyscallHandlerCallbacks::new(&mut syscall_handler); - for b in &self.contract_info.entry_points[&function_id.id].builtins { + for b in &self.entry_points_info[&function_id.id].builtins { match b { BuiltinType::Gas => { let gas = gas.unwrap_or(0); gas.to_bytes(&mut invoke_data)?; } - BuiltinType::BuiltinCosts => { - builtin_costs_ptr.to_bytes(&mut invoke_data)?; - } BuiltinType::System => { (&mut syscall_handler as *mut StarknetSyscallHandlerCallbacks<_>) .to_bytes(&mut invoke_data)?; @@ -347,7 +311,7 @@ impl AotContractExecutor { let return_ptr = &mut return_ptr.cast(); - for b in &self.contract_info.entry_points[&function_id.id].builtins { + for b in &self.entry_points_info[&function_id.id].builtins { match b { BuiltinType::Gas => { remaining_gas = unsafe { *read_value::(return_ptr) }; @@ -356,11 +320,6 @@ impl AotContractExecutor { let ptr = return_ptr.cast::<*mut ()>(); *return_ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().add(1)).cast() }; } - BuiltinType::BuiltinCosts => { - let ptr = return_ptr.cast::<*mut ()>(); - *return_ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().add(1)).cast() }; - // ptr holds the builtin costs, but they dont change, so its of no use, but we read to advance the ptr. - } x => { let value = unsafe { *read_value::(return_ptr) } as usize; @@ -376,7 +335,6 @@ impl AotContractExecutor { BuiltinType::CircuitMul => builtin_stats.circuit_mul = value, BuiltinType::Gas => {} BuiltinType::System => {} - BuiltinType::BuiltinCosts => {} } } } @@ -478,15 +436,6 @@ impl AotContractExecutor { .into_raw() }) } - - pub fn find_symbol_ptr(&self, name: &str) -> Option<*mut c_void> { - unsafe { - self.library - .get::<*mut ()>(name.as_bytes()) - .ok() - .map(|x| x.into_raw().into_raw()) - } - } } impl Drop for AotContractExecutor { @@ -570,7 +519,6 @@ mod tests { entrypoint_function_id, &[2.into()], Some(u64::MAX as u128), - None, &mut StubSyscallHandler::default(), ) .unwrap(); @@ -596,7 +544,6 @@ mod tests { entrypoint_function_id, &[], Some(u64::MAX as u128), - None, &mut StubSyscallHandler::default(), ) .unwrap(); diff --git a/src/executor/jit.rs b/src/executor/jit.rs index 4b50ce113..55bfafcbe 100644 --- a/src/executor/jit.rs +++ b/src/executor/jit.rs @@ -79,7 +79,6 @@ impl<'m> JitNativeExecutor<'m> { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), - self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id).unwrap(), args, available_gas, @@ -103,7 +102,6 @@ impl<'m> JitNativeExecutor<'m> { super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), - self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id).unwrap(), args, available_gas, @@ -126,7 +124,6 @@ impl<'m> JitNativeExecutor<'m> { ContractExecutionResult::from_execution_result(super::invoke_dynamic( &self.registry, self.find_function_ptr(function_id), - self.find_symbol_ptr("builtin_costs"), self.extract_signature(function_id).unwrap(), &[Value::Struct { fields: vec![Value::Array( @@ -148,16 +145,6 @@ impl<'m> JitNativeExecutor<'m> { self.engine.lookup(&function_name) as *mut c_void } - pub fn find_symbol_ptr(&self, name: &str) -> Option<*mut c_void> { - let ptr = self.engine.lookup(name) as *mut c_void; - - if ptr.is_null() { - None - } else { - Some(ptr) - } - } - fn extract_signature(&self, function_id: &FunctionId) -> Option<&FunctionSignature> { self.program_registry() .get_function(function_id) diff --git a/src/libfuncs/gas.rs b/src/libfuncs/gas.rs index b7eabecc9..39587d237 100644 --- a/src/libfuncs/gas.rs +++ b/src/libfuncs/gas.rs @@ -9,7 +9,7 @@ use crate::{ use cairo_lang_sierra::{ extensions::{ core::{CoreLibfunc, CoreType}, - gas::{CostTokenType, GasConcreteLibfunc}, + gas::GasConcreteLibfunc, lib_func::SignatureOnlyConcreteLibfunc, ConcreteLibfunc, }, @@ -18,10 +18,9 @@ use cairo_lang_sierra::{ use melior::{ dialect::{ arith::{self, CmpiPredicate}, - llvm::{self, r#type::pointer}, - ods, + llvm, ods, }, - ir::{attribute::FlatSymbolRefAttribute, r#type::IntegerType, Block, Location}, + ir::{r#type::IntegerType, Block, Location}, Context, }; @@ -84,74 +83,22 @@ pub fn build_withdraw_gas<'ctx, 'this>( super::increment_builtin_counter(context, entry, location, entry.argument(0)?.into())?; let current_gas = entry.argument(1)?.into(); - let gas_cost = metadata - .get::() - .expect("builtin_withdraw_gas should always have a gas cost"); + let cost = metadata.get::().and_then(|x| x.0); let u128_type: melior::ir::Type = IntegerType::new(context, 128).into(); - let u64_type: melior::ir::Type = IntegerType::new(context, 64).into(); - - // Get the ptr to the global, holding a ptr to the list. - let builtin_ptr_ptr = entry.append_op_result( - ods::llvm::mlir_addressof( - context, - pointer(context, 0), - FlatSymbolRefAttribute::new(context, "builtin_costs"), - location, - ) - .into(), - )?; - - let builtin_ptr = entry.load(context, location, builtin_ptr_ptr, pointer(context, 0))?; - - let mut final_gas_cost = entry.const_int_from_type(context, location, 0, u128_type)?; - - for (cost, token_type) in &gas_cost.0 { - if *cost == 0 { - continue; - } - - let token_type_index = match token_type { - CostTokenType::Const => 0, - CostTokenType::Pedersen => 1, - CostTokenType::Bitwise => 2, - CostTokenType::EcOp => 3, - CostTokenType::Poseidon => 4, - CostTokenType::AddMod => 5, - CostTokenType::MulMod => 6, - _ => unreachable!(), - }; - - let gas_cost_val = entry.const_int_from_type(context, location, *cost, u128_type)?; - let token_type_index_val = - entry.const_int_from_type(context, location, token_type_index, u64_type)?; - - let cost_value_ptr = entry.append_op_result(llvm::get_element_ptr_dynamic( - context, - builtin_ptr, - &[token_type_index_val], - u64_type, - pointer(context, 0), - location, - ))?; - let cost_value = entry.load(context, location, cost_value_ptr, u64_type)?; - let cost_value = entry.append_op_result(arith::extui(cost_value, u128_type, location))?; - let total_gas_cost_val = - entry.append_op_result(arith::muli(gas_cost_val, cost_value, location))?; - final_gas_cost = - entry.append_op_result(arith::addi(final_gas_cost, total_gas_cost_val, location))?; - } + let gas_cost_val = + entry.const_int_from_type(context, location, cost.unwrap_or(0), u128_type)?; let is_enough = entry.append_op_result(arith::cmpi( context, CmpiPredicate::Uge, current_gas, - final_gas_cost, + gas_cost_val, location, ))?; let resulting_gas = entry.append_op_result( - ods::llvm::intr_usub_sat(context, current_gas, final_gas_cost, location).into(), + ods::llvm::intr_usub_sat(context, current_gas, gas_cost_val, location).into(), )?; entry.append_operation(helper.cond_br( @@ -178,63 +125,22 @@ pub fn build_builtin_withdraw_gas<'ctx, 'this>( let range_check = super::increment_builtin_counter(context, entry, location, entry.argument(0)?.into())?; let current_gas = entry.argument(1)?.into(); - let builtin_ptr = entry.argument(2)?.into(); - - let gas_cost = metadata - .get::() - .expect("builtin_withdraw_gas should always have a gas cost"); + let cost = metadata.get::().and_then(|x| x.0); let u128_type: melior::ir::Type = IntegerType::new(context, 128).into(); - let u64_type: melior::ir::Type = IntegerType::new(context, 64).into(); - - let mut final_gas_cost = entry.const_int_from_type(context, location, 0, u128_type)?; - - for (cost, token_type) in &gas_cost.0 { - if *cost == 0 { - continue; - } - - let token_type_index = match token_type { - CostTokenType::Const => 0, - CostTokenType::Pedersen => 1, - CostTokenType::Bitwise => 2, - CostTokenType::EcOp => 3, - CostTokenType::Poseidon => 4, - CostTokenType::AddMod => 5, - CostTokenType::MulMod => 6, - _ => unreachable!(), - }; - - let gas_cost_val = entry.const_int_from_type(context, location, *cost, u128_type)?; - let token_type_index_val = - entry.const_int_from_type(context, location, token_type_index, u64_type)?; - - let cost_value_ptr = entry.append_op_result(llvm::get_element_ptr_dynamic( - context, - builtin_ptr, - &[token_type_index_val], - u64_type, - pointer(context, 0), - location, - ))?; - let cost_value = entry.load(context, location, cost_value_ptr, u64_type)?; - let cost_value = entry.append_op_result(arith::extui(cost_value, u128_type, location))?; - let total_gas_cost_val = - entry.append_op_result(arith::muli(gas_cost_val, cost_value, location))?; - final_gas_cost = - entry.append_op_result(arith::addi(final_gas_cost, total_gas_cost_val, location))?; - } + let gas_cost_val = + entry.const_int_from_type(context, location, cost.unwrap_or(0), u128_type)?; let is_enough = entry.append_op_result(arith::cmpi( context, CmpiPredicate::Uge, current_gas, - final_gas_cost, + gas_cost_val, location, ))?; let resulting_gas = entry.append_op_result( - ods::llvm::intr_usub_sat(context, current_gas, final_gas_cost, location).into(), + ods::llvm::intr_usub_sat(context, current_gas, gas_cost_val, location).into(), )?; entry.append_operation(helper.cond_br( @@ -266,20 +172,10 @@ pub fn build_get_builtin_costs<'ctx, 'this>( &info.branch_signatures()[0].vars[0].ty, )?; - // Get the ptr to the global, holding a ptr to the list. - let builtin_ptr_ptr = entry.append_op_result( - ods::llvm::mlir_addressof( - context, - pointer(context, 0), - FlatSymbolRefAttribute::new(context, "builtin_costs"), - location, - ) - .into(), - )?; - - let builtin_ptr = entry.load(context, location, builtin_ptr_ptr, builtin_costs_ty)?; + // TODO: Implement libfunc. + let op0 = entry.append_op_result(llvm::undef(builtin_costs_ty, location))?; - entry.append_operation(helper.br(0, &[builtin_ptr], location)); + entry.append_operation(helper.br(0, &[op0], location)); Ok(()) } diff --git a/src/metadata/gas.rs b/src/metadata/gas.rs index 9bb2ac34b..9378c5895 100644 --- a/src/metadata/gas.rs +++ b/src/metadata/gas.rs @@ -20,9 +20,8 @@ pub struct GasMetadata { pub gas_info: GasInfo, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -// Cost, token type (index into builtin costs). -pub struct GasCost(pub Vec<(u128, CostTokenType)>); +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GasCost(pub Option); /// Configuration for metadata computation. #[derive(Debug, Clone)] @@ -103,18 +102,16 @@ impl GasMetadata { ) } - pub fn get_gas_costs_for_statement(&self, idx: StatementIdx) -> Vec<(u128, CostTokenType)> { - let mut costs = Vec::new(); + pub fn get_gas_cost_for_statement(&self, idx: StatementIdx) -> Option { + let mut cost = None; for cost_type in CostTokenType::iter_casm_tokens() { if let Some(amount) = self.get_gas_cost_for_statement_and_cost_token_type(idx, *cost_type) { - if amount > 0 { - costs.push((amount, *cost_type)); - } + *cost.get_or_insert(0) += amount * token_gas_cost(*cost_type) as u128; } } - costs + cost } pub fn get_gas_cost_for_statement_and_cost_token_type( diff --git a/src/types.rs b/src/types.rs index 6d0d6314e..ab364e487 100644 --- a/src/types.rs +++ b/src/types.rs @@ -550,11 +550,10 @@ impl TypeBuilder for CoreTypeConcrete { | CoreTypeConcrete::RangeCheck96(_) | CoreTypeConcrete::SegmentArena(_) => false, - // A ptr to a list of costs. - CoreTypeConcrete::BuiltinCosts(_) => false, - // Other builtins: - CoreTypeConcrete::Uint128MulGuarantee(_) | CoreTypeConcrete::Coupon(_) => true, + CoreTypeConcrete::BuiltinCosts(_) + | CoreTypeConcrete::Uint128MulGuarantee(_) + | CoreTypeConcrete::Coupon(_) => true, // Normal types: CoreTypeConcrete::Array(_) @@ -636,7 +635,7 @@ impl TypeBuilder for CoreTypeConcrete { CoreTypeConcrete::EcState(_) => layout_repeat(&get_integer_layout(252), 4)?.0, CoreTypeConcrete::Felt252(_) => get_integer_layout(252), CoreTypeConcrete::GasBuiltin(_) => get_integer_layout(128), - CoreTypeConcrete::BuiltinCosts(_) => Layout::new::<*const ()>(), + CoreTypeConcrete::BuiltinCosts(_) => Layout::new::<()>(), CoreTypeConcrete::Uint8(_) => get_integer_layout(8), CoreTypeConcrete::Uint16(_) => get_integer_layout(16), CoreTypeConcrete::Uint32(_) => get_integer_layout(32), diff --git a/src/types/builtin_costs.rs b/src/types/builtin_costs.rs index 7fc2e978c..b7a202dce 100644 --- a/src/types/builtin_costs.rs +++ b/src/types/builtin_costs.rs @@ -1,7 +1,4 @@ //! # Builtin costs type -//! -//! A ptr to a list of u64, this list will not change at runtime in size and thus we only really need to store the pointer, -//! it can be allocated on the stack on rust side and passed. use super::WithSelf; use crate::{error::Result, metadata::MetadataStorage}; @@ -14,7 +11,7 @@ use cairo_lang_sierra::{ }; use melior::{ dialect::llvm, - ir::{Module, Type}, + ir::{r#type::IntegerType, Module, Type}, Context, }; @@ -28,6 +25,5 @@ pub fn build<'ctx>( _metadata: &mut MetadataStorage, _info: WithSelf, ) -> Result> { - // A ptr to a list of u64 - Ok(llvm::r#type::pointer(context, 0)) + Ok(llvm::r#type::array(IntegerType::new(context, 8).into(), 0)) } diff --git a/src/utils.rs b/src/utils.rs index 9dc73cb4d..2a26eb930 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,9 +5,7 @@ pub(crate) use self::{ }; use crate::{metadata::MetadataStorage, OptLevel}; use cairo_lang_compiler::CompilerConfig; -use cairo_lang_runner::token_gas_cost; use cairo_lang_sierra::{ - extensions::gas::CostTokenType, ids::FunctionId, program::{GenFunction, Program, StatementIdx}, }; @@ -17,7 +15,6 @@ use melior::{ Context, Error, ExecutionEngine, }; use num_bigint::{BigInt, BigUint, Sign}; -use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; use std::sync::LazyLock; use std::{ @@ -52,47 +49,6 @@ pub static HALF_PRIME: LazyLock = LazyLock::new(|| { .unwrap() }); -#[derive(Debug, Clone, Copy, Deserialize, Serialize)] -pub struct BuiltinCosts { - pub r#const: u64, - pub pedersen: u64, - pub bitwise: u64, - pub ecop: u64, - pub poseidon: u64, - pub add_mod: u64, - pub mul_mod: u64, -} - -impl From for [u64; 7] { - // Order matters, for the libfunc impl - // https://github.com/starkware-libs/sequencer/blob/1b7252f8a30244d39614d7666aa113b81291808e/crates/blockifier/src/execution/entry_point_execution.rs#L208 - fn from(value: BuiltinCosts) -> Self { - [ - value.r#const, - value.pedersen, - value.bitwise, - value.ecop, - value.poseidon, - value.add_mod, - value.mul_mod, - ] - } -} - -impl Default for BuiltinCosts { - fn default() -> Self { - Self { - r#const: token_gas_cost(CostTokenType::Const) as u64, - pedersen: token_gas_cost(CostTokenType::Pedersen) as u64, - bitwise: token_gas_cost(CostTokenType::Bitwise) as u64, - ecop: token_gas_cost(CostTokenType::EcOp) as u64, - poseidon: token_gas_cost(CostTokenType::Poseidon) as u64, - add_mod: token_gas_cost(CostTokenType::AddMod) as u64, - mul_mod: token_gas_cost(CostTokenType::MulMod) as u64, - } - } -} - #[cfg(feature = "with-mem-tracing")] #[allow(unused_imports)] pub(crate) use self::mem_tracing::{ diff --git a/src/values.rs b/src/values.rs index 796933dec..213360111 100644 --- a/src/values.rs +++ b/src/values.rs @@ -1,6 +1,6 @@ -//! # Params and return values de/serialization +//! # JIT params and return values de/serialization //! -//! A Rusty interface to provide parameters to cairo-native entry point calls. +//! A Rusty interface to provide parameters to JIT calls. use crate::{ error::{CompilerError, Error}, diff --git a/tests/common.rs b/tests/common.rs index 9ca95d52b..f0cac40da 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -391,6 +391,7 @@ pub fn run_vm_contract( .collect_vec() } +#[track_caller] pub fn compare_inputless_program(program_path: &str) { let program: (String, Program, SierraCasmRunner) = load_cairo_path(program_path); let program = &program; @@ -437,6 +438,7 @@ pub fn run_native_starknet_contract( /// the results automatically, triggering a proptest assert if there is a mismatch. /// /// Left of report of the assert is the cairo vm result, right side is cairo native +#[track_caller] pub fn compare_outputs( program: &Program, entry_point: &FunctionId, @@ -742,12 +744,8 @@ pub fn compare_outputs( .unwrap_or(false) }); assert_eq!( - vm_result - .gas_counter - .unwrap_or_else(|| Felt::from(0)) - .to_bigint(), - Felt::from(native_result.remaining_gas.unwrap_or(0)).to_bigint(), - "gas mismatch" + vm_result.gas_counter.unwrap_or_else(|| Felt::from(0)), + Felt::from(native_result.remaining_gas.unwrap_or(0)), ); let vm_result = match &vm_result.value { @@ -809,11 +807,7 @@ pub fn compare_outputs( }, }; - pretty_assertions_sorted::assert_eq!( - native_result.return_value, - vm_result, - "return value mismatch" - ); + pretty_assertions_sorted::assert_eq!(native_result.return_value, vm_result); Ok(()) } From 567c7a1da3d91dc3e95c2a6ee1b5504f3f9d2a09 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 30 Oct 2024 16:35:22 -0300 Subject: [PATCH 13/17] fix seg fault felt dict --- Cargo.lock | 20 ++++++++++---------- runtime/src/lib.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3422926a..ecae615c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2333,9 +2333,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -3206,9 +3206,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags", "errno", @@ -3252,9 +3252,9 @@ dependencies = [ [[package]] name = "scarb-metadata" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170ebce1774a85568646ba4096827f898306665187eebd9282fee313e316518d" +checksum = "1a8b71f63999dbb6d269fbc6fd61310016ab3a160fb13e52a6511a2b904359f0" dependencies = [ "camino", "semver", @@ -3336,18 +3336,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1e16a3726..062315ebf 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -989,7 +989,7 @@ pub mod trace_dump { CoreTypeConcrete::RangeCheck96(_) => todo!("CoreTypeConcrete::RangeCheck96"), CoreTypeConcrete::Felt252Dict(info) => { - let value = value_ptr.cast::().as_ref(); + let value = value_ptr.cast::<&FeltDict>().read(); let data = value .inner From 99accfb34309ea70233666aa5e105022a0c921f3 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 31 Oct 2024 11:47:50 -0300 Subject: [PATCH 14/17] implement feltDictEntry and SquashedFeltDict --- runtime/src/lib.rs | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 062315ebf..5fcd1e090 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -676,14 +676,13 @@ pub mod trace_dump { use std::{ alloc::Layout, collections::HashMap, - marker::PhantomData, mem::swap, ops::Range, ptr::NonNull, sync::{LazyLock, Mutex}, }; - use crate::FeltDict; + use crate::{c_void, FeltDict}; pub static TRACE_DUMP: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -963,6 +962,7 @@ pub mod trace_dump { | CoreTypeConcrete::EcOp(_) | CoreTypeConcrete::Pedersen(_) | CoreTypeConcrete::Poseidon(_) + | CoreTypeConcrete::RangeCheck96(_) | CoreTypeConcrete::RangeCheck(_) | CoreTypeConcrete::SegmentArena(_) | CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::System(_)) @@ -987,8 +987,8 @@ pub mod trace_dump { } } - CoreTypeConcrete::RangeCheck96(_) => todo!("CoreTypeConcrete::RangeCheck96"), - CoreTypeConcrete::Felt252Dict(info) => { + CoreTypeConcrete::SquashedFelt252Dict(info) + | CoreTypeConcrete::Felt252Dict(info) => { let value = value_ptr.cast::<&FeltDict>().read(); let data = value @@ -1013,9 +1013,33 @@ pub mod trace_dump { data, } } - CoreTypeConcrete::Felt252DictEntry(_) => todo!("CoreTypeConcrete::Felt252DictEntry"), - CoreTypeConcrete::SquashedFelt252Dict(_) => { - todo!("CoreTypeConcrete::SquashedFelt252Dict") + CoreTypeConcrete::Felt252DictEntry(info) => { + let value = value_ptr.cast::().read(); + + let data = value + .dict + .inner + .iter() + .map(|(k, &p)| { + let v = match NonNull::new(p) { + Some(value_ptr) => { + read_value_ptr(registry, &info.ty, value_ptr.cast(), get_layout) + } + None => Value::Uninitialized { + ty: info.ty.clone(), + }, + }; + let k = Felt::from_bytes_le(k); + (k, v) + }) + .collect::>(); + let key = Felt::from_bytes_le(&value.key); + + Value::FeltDictEntry { + ty: info.ty.clone(), + data, + key + } } CoreTypeConcrete::Span(_) => todo!("CoreTypeConcrete::Span"), CoreTypeConcrete::StarkNet(selector) => match selector { @@ -1068,6 +1092,12 @@ pub mod trace_dump { } } + #[derive(Debug)] + struct FeltDictEntry<'a> { + dict: &'a FeltDict, + key: &'a [u8; 32] + } + #[repr(C, align(16))] pub struct Secp256Point { pub x: U256, From 85ce88b0ad7440b5a07f6c3fd984bf4e1ec90517 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 19 Nov 2024 16:17:41 -0300 Subject: [PATCH 15/17] gas u64 --- runtime/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1c08ee012..4cda6a02b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -794,10 +794,10 @@ pub mod trace_dump { CoreTypeConcrete::Uint8(_) => Value::U8(value_ptr.cast().read()), CoreTypeConcrete::Uint16(_) => Value::U16(value_ptr.cast().read()), CoreTypeConcrete::Uint32(_) => Value::U32(value_ptr.cast().read()), - CoreTypeConcrete::Uint64(_) => Value::U64(value_ptr.cast().read()), - CoreTypeConcrete::Uint128(_) | CoreTypeConcrete::GasBuiltin(_) => { - Value::U128(value_ptr.cast().read()) + CoreTypeConcrete::Uint64(_) | CoreTypeConcrete::GasBuiltin(_) => { + Value::U64(value_ptr.cast().read()) } + CoreTypeConcrete::Uint128(_) => Value::U128(value_ptr.cast().read()), CoreTypeConcrete::BoundedInt(BoundedIntConcreteType { range, .. }) => { let n_bits = ((range.size() - BigInt::one()).bits() as u32).max(1); From 8411019f8cebd2ce261a94ef366acf961c0259b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Gonz=C3=A1lez=20Calder=C3=B3n?= Date: Mon, 30 Dec 2024 11:11:25 -0300 Subject: [PATCH 16/17] Adapt trace dump with new dictionary --- runtime/src/lib.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 7b4f6ff4a..bc8ccf1c6 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1103,9 +1103,12 @@ pub mod trace_dump { let value = value_ptr.cast::<&FeltDict>().read(); let data = value - .inner + .mappings .iter() - .map(|(k, &p)| { + .map(|(k, &i)| { + let p = value + .elements + .byte_offset((value.layout.size() * i) as isize); let v = match NonNull::new(p) { Some(value_ptr) => { read_value_ptr(registry, &info.ty, value_ptr.cast(), get_layout) @@ -1129,9 +1132,13 @@ pub mod trace_dump { let data = value .dict - .inner + .mappings .iter() - .map(|(k, &p)| { + .map(|(k, &i)| { + let p = value + .dict + .elements + .byte_offset((value.dict.layout.size() * i) as isize); let v = match NonNull::new(p) { Some(value_ptr) => { read_value_ptr(registry, &info.ty, value_ptr.cast(), get_layout) @@ -1200,6 +1207,7 @@ pub mod trace_dump { Value::Bytes31(Felt::from_bytes_le(&data)) } + CoreTypeConcrete::IntRange(_) => todo!(), } } From c2529e160d7c6cee849ee2a7ac0f3009bdecafbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Gonz=C3=A1lez=20Calder=C3=B3n?= Date: Fri, 17 Jan 2025 18:14:31 -0300 Subject: [PATCH 17/17] Use local path --- runtime/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 6972ba422..3d96e8ef4 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -30,6 +30,6 @@ rand = "0.8.5" num-bigint = { version = "0.4.4", optional = true } cairo-lang-sierra = { version = "=2.10.0-rc.0", optional = true } cairo-lang-utils = { version = "=2.10.0-rc.0", optional = true } -sierra-emu = { git = "https://github.com/lambdaclass/sierra-emu.git", rev = "70b392fd816cd6c4a55410f9936fe62eaba85713", optional = true } +sierra-emu = { path = "../../sierra-emu", optional = true } itertools = "0.14.0" num-traits = "0.2"