Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented iterable squashed felt252 dict #7289

Merged
merged 1 commit into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions corelib/src/dict.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ pub(crate) extern fn felt252_dict_squash<T>(
dict: Felt252Dict<T>,
) -> SquashedFelt252Dict<T> implicits(RangeCheck, GasBuiltin, SegmentArena) nopanic;

extern fn squashed_felt252_dict_entries<T>(
dict: SquashedFelt252Dict<T>,
) -> Array<(felt252, T, T)> nopanic;

/// Basic trait for the `Felt252Dict` type.
pub trait Felt252DictTrait<T> {
/// Inserts the given value for the given key.
Expand Down Expand Up @@ -286,3 +290,20 @@ impl Felt252DictFromIterator<
dict
}
}

/// Basic trait for the `SquashedFelt252Dict` type.
#[generate_trait]
pub impl SquashedFelt252DictImpl<T> of SquashedFelt252DictTrait<T> {
/// Returns an array of `(key, first_value, last_value)` tuples.
/// The first value is always 0.
///
/// # Example
/// ```
/// let squashed_dict = dict.squash();
/// let entries = squashed_dict.entries();
/// ```
#[inline]
fn into_entries(self: SquashedFelt252Dict<T>) -> Array<(felt252, T, T)> {
squashed_felt252_dict_entries(self)
}
}
10 changes: 9 additions & 1 deletion corelib/src/test/dict_test.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::dict::{Felt252Dict, Felt252DictEntryTrait};
use crate::dict::{Felt252Dict, Felt252DictEntryTrait, SquashedFelt252DictImpl};
use crate::test::test_utils::assert_eq;
use crate::nullable;

Expand Down Expand Up @@ -176,3 +176,11 @@ fn test_dict_from_collect_with_duplicate_keys() {
let mut dict = array![(0, 1_u32), (0, 2_u32)].into_iter().collect::<Felt252Dict<_>>();
assert_eq!(dict[0], 2);
}

#[test]
fn test_array_from_squash_dict() {
let mut dict: Felt252Dict<u32> = (0..5_u32).into_iter().map(|x| (x.into(), x)).collect();
assert_eq!(
dict.squash().into_entries(), array![(0, 0, 0), (1, 0, 1), (2, 0, 2), (3, 0, 3), (4, 0, 4)],
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
}
Felt252Concrete::IsZero(_) => vec![ApChange::Known(0), ApChange::Known(0)],
},
Felt252SquashedDict(_) => {
vec![ApChange::Known(0)]
}
FunctionCall(libfunc) | CouponCall(libfunc) => {
vec![ApChange::FunctionCall(libfunc.function.id.clone())]
}
Expand Down
1 change: 1 addition & 0 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ pub fn core_libfunc_cost(
vec![DICT_SQUASH_FIXED_COST.into()]
}
},
Felt252SquashedDict(_) => vec![ConstCost::default().into()],
Pedersen(libfunc) => match libfunc {
PedersenConcreteLibfunc::PedersenHash(_) => {
vec![BranchCost::Regular {
Expand Down
2 changes: 2 additions & 0 deletions crates/cairo-lang-sierra-to-casm/src/invocations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ mod pedersen;
mod poseidon;
mod range;
mod range_reduction;
mod squashed_felt252_dict;
mod starknet;
mod structure;

Expand Down Expand Up @@ -654,6 +655,7 @@ pub fn compile_invocation(
CompiledInvocationBuilder { program_info, invocation, libfunc, idx, refs, environment };
match libfunc {
Felt252(libfunc) => felt252::build(libfunc, builder),
Felt252SquashedDict(libfunc) => squashed_felt252_dict::build(libfunc, builder),
Bool(libfunc) => boolean::build(libfunc, builder),
Cast(libfunc) => casts::build(libfunc, builder),
Ec(libfunc) => ec::build(libfunc, builder),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use cairo_lang_sierra::extensions::squashed_felt252_dict::SquashedFelt252DictConcreteLibfunc;

use super::misc::build_identity;
use super::{CompiledInvocation, CompiledInvocationBuilder, InvocationError};

/// Builds instructions for Sierra squashed dict operations.
pub fn build(
libfunc: &SquashedFelt252DictConcreteLibfunc,
builder: CompiledInvocationBuilder<'_>,
) -> Result<CompiledInvocation, InvocationError> {
match libfunc {
SquashedFelt252DictConcreteLibfunc::IntoEntries(_) => build_identity(builder),
}
}
3 changes: 2 additions & 1 deletion crates/cairo-lang-sierra/src/extensions/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use super::range_check::{RangeCheck96Type, RangeCheckType};
use super::segment_arena::SegmentArenaType;
use super::snapshot::{SnapshotTakeLibfunc, SnapshotType};
use super::span::SpanType;
use super::squashed_felt252_dict::SquashedFelt252DictType;
use super::squashed_felt252_dict::{SquashedFelt252DictLibfunc, SquashedFelt252DictType};
use super::starknet::{StarknetLibfunc, StarknetType};
use super::structure::{StructLibfunc, StructType};
use super::uninitialized::UninitializedType;
Expand Down Expand Up @@ -141,6 +141,7 @@ define_libfunc_hierarchy! {
Struct(StructLibfunc),
Felt252Dict(Felt252DictLibfunc),
Felt252DictEntry(Felt252DictEntryLibfunc),
Felt252SquashedDict(SquashedFelt252DictLibfunc),
Pedersen(PedersenLibfunc),
Poseidon(PoseidonLibfunc),
Starknet(StarknetLibfunc),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
use crate::extensions::SpecializationError;
use super::array::ArrayType;
use super::felt252::Felt252Type;
use super::structure::StructType;
use super::utils::reinterpret_cast_signature;
use crate::define_libfunc_hierarchy;
use crate::extensions::lib_func::{
LibfuncSignature, SignatureAndTypeGenericLibfunc, SignatureSpecializationContext,
WrapSignatureAndTypeGenericLibfunc,
};
use crate::extensions::type_specialization_context::TypeSpecializationContext;
use crate::extensions::types::{
GenericTypeArgGenericType, GenericTypeArgGenericTypeWrapper, TypeInfo,
};
use crate::ids::GenericTypeId;
use crate::extensions::{NamedType, SpecializationError};
use crate::ids::{ConcreteTypeId, GenericTypeId, UserTypeId};
use crate::program::GenericArg;

define_libfunc_hierarchy! {
pub enum SquashedFelt252DictLibfunc {
IntoEntries(SquashedDictIntoEntriesLibfunc),
}, SquashedFelt252DictConcreteLibfunc
}
/// Type representing a static squashed dictionary from a felt252 to any type of size one.
#[derive(Default)]
pub struct SquashedFelt252DictTypeWrapped {}
Expand Down Expand Up @@ -35,3 +50,29 @@ impl GenericTypeArgGenericType for SquashedFelt252DictTypeWrapped {
}
}
pub type SquashedFelt252DictType = GenericTypeArgGenericTypeWrapper<SquashedFelt252DictTypeWrapped>;

#[derive(Default)]
pub struct SquashedDictAsEntriesLibfuncWrapped;
impl SignatureAndTypeGenericLibfunc for SquashedDictAsEntriesLibfuncWrapped {
const STR_ID: &'static str = "squashed_felt252_dict_entries";

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let squashed_dict_ty =
context.get_wrapped_concrete_type(SquashedFelt252DictType::id(), ty.clone())?;
let felt252_ty = context.get_concrete_type(Felt252Type::id(), &[])?;
let tuple_ty = context.get_concrete_type(StructType::id(), &[
GenericArg::UserType(UserTypeId::from_string("Tuple")),
GenericArg::Type(felt252_ty.clone()),
GenericArg::Type(ty.clone()),
GenericArg::Type(ty.clone()),
])?;
let array_ty = context.get_wrapped_concrete_type(ArrayType::id(), tuple_ty)?;
Ok(reinterpret_cast_signature(squashed_dict_ty, array_ty))
}
}
pub type SquashedDictIntoEntriesLibfunc =
WrapSignatureAndTypeGenericLibfunc<SquashedDictAsEntriesLibfuncWrapped>;
3 changes: 3 additions & 0 deletions crates/cairo-lang-sierra/src/simulation/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ pub fn simulate<
// Returning the same dict since it is exactly the same as the squashed one.
(vec![CoreValue::RangeCheck, CoreValue::Dict(dict)], 0)
}
CoreConcreteLibfunc::Felt252SquashedDict(_) => {
unimplemented!("Simulation of Felt252SquashedDict is not implemented yet.");
}
CoreConcreteLibfunc::Pedersen(_) => {
unimplemented!("Simulation of the Pedersen hash function is not implemented yet.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@
"send_message_to_l1_syscall",
"snapshot_take",
"span_from_tuple",
"squashed_felt252_dict_entries",
"storage_address_from_base",
"storage_address_from_base_and_offset",
"storage_address_to_felt252",
Expand Down
86 changes: 86 additions & 0 deletions tests/e2e_test_data/libfuncs/felt252_dict
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,89 @@ store_temp<Tuple<Felt252Dict<felt252>, felt252, felt252>>([11]) -> ([11]); // 5
return([11]); // 6

test::foo@0([0]: Felt252Dict<felt252>, [1]: felt252, [2]: felt252, [3]: felt252, [4]: felt252) -> (Tuple<Felt252Dict<felt252>, felt252, felt252>);

//! > ==========================================================================

//! > felt252_dict_squash_entries libfunc

//! > test_comments

//! > test_runner_name
SmallE2ETestRunner

//! > cairo_code
fn test_squashed_dict_entries(
dict: SquashedFelt252Dict<felt252>,
) -> Array<(felt252, felt252, felt252)> {
dict::SquashedFelt252DictImpl::<felt252>::into_entries(dict)
}

//! > casm
[ap + 0] = [fp + -4], ap++;
[ap + 0] = [fp + -3], ap++;
ret;

//! > sierra_code
type SquashedFelt252Dict<felt252> = SquashedFelt252Dict<felt252> [storable: true, drop: true, dup: false, zero_sized: false];
type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false];
type Array<Tuple<felt252, felt252, felt252>> = Array<Tuple<felt252, felt252, felt252>> [storable: true, drop: true, dup: false, zero_sized: false];
type Tuple<felt252, felt252, felt252> = Struct<ut@Tuple, felt252, felt252, felt252> [storable: true, drop: true, dup: true, zero_sized: false];

libfunc squashed_felt252_dict_entries<felt252> = squashed_felt252_dict_entries<felt252>;
libfunc store_temp<Array<Tuple<felt252, felt252, felt252>>> = store_temp<Array<Tuple<felt252, felt252, felt252>>>;

squashed_felt252_dict_entries<felt252>([0]) -> ([1]); // 0
store_temp<Array<Tuple<felt252, felt252, felt252>>>([1]) -> ([1]); // 1
return([1]); // 2

test::test_squashed_dict_entries@0([0]: SquashedFelt252Dict<felt252>) -> (Array<Tuple<felt252, felt252, felt252>>);

//! > function_costs
test::test_squashed_dict_entries: OrderedHashMap({Const: 200})

//! > ==========================================================================

//! > Test dict get with nullable

//! > test_comments

//! > test_runner_name
SmallE2ETestRunner

//! > cairo_code
fn test_squashed_dict_entries(
dict: SquashedFelt252Dict<Nullable<u256>>,
) -> core::option::Option<core::array::Array<(felt252, Nullable<u256>, Nullable<u256>)>> {
Some(dict::SquashedFelt252DictImpl::<Nullable<u256>>::into_entries(dict))
}

//! > casm
[ap + 0] = 0, ap++;
[ap + 0] = [fp + -4], ap++;
[ap + 0] = [fp + -3], ap++;
ret;

//! > sierra_code
type SquashedFelt252Dict<Nullable<core::integer::u256>> = SquashedFelt252Dict<Nullable<core::integer::u256>> [storable: true, drop: true, dup: false, zero_sized: false];
type Array<Tuple<felt252, Nullable<core::integer::u256>, Nullable<core::integer::u256>>> = Array<Tuple<felt252, Nullable<core::integer::u256>, Nullable<core::integer::u256>>> [storable: true, drop: true, dup: false, zero_sized: false];
type Unit = Struct<ut@Tuple> [storable: true, drop: true, dup: true, zero_sized: true];
type core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>> = Enum<ut@core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>, Array<Tuple<felt252, Nullable<core::integer::u256>, Nullable<core::integer::u256>>>, Unit> [storable: true, drop: true, dup: false, zero_sized: false];
type Nullable<core::integer::u256> = Nullable<core::integer::u256> [storable: true, drop: true, dup: true, zero_sized: false];
type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false];
type Tuple<felt252, Nullable<core::integer::u256>, Nullable<core::integer::u256>> = Struct<ut@Tuple, felt252, Nullable<core::integer::u256>, Nullable<core::integer::u256>> [storable: true, drop: true, dup: true, zero_sized: false];
type u128 = u128 [storable: true, drop: true, dup: true, zero_sized: false];
type core::integer::u256 = Struct<ut@core::integer::u256, u128, u128> [storable: true, drop: true, dup: true, zero_sized: false];

libfunc squashed_felt252_dict_entries<Nullable<core::integer::u256>> = squashed_felt252_dict_entries<Nullable<core::integer::u256>>;
libfunc enum_init<core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>, 0> = enum_init<core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>, 0>;
libfunc store_temp<core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>> = store_temp<core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>>;

squashed_felt252_dict_entries<Nullable<core::integer::u256>>([0]) -> ([1]); // 0
enum_init<core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>, 0>([1]) -> ([2]); // 1
store_temp<core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>>([2]) -> ([2]); // 2
return([2]); // 3

test::test_squashed_dict_entries@0([0]: SquashedFelt252Dict<Nullable<core::integer::u256>>) -> (core::option::Option::<core::array::Array::<(core::felt252, core::nullable::Nullable::<core::integer::u256>, core::nullable::Nullable::<core::integer::u256>)>>);

//! > function_costs
test::test_squashed_dict_entries: OrderedHashMap({Const: 300})