diff --git a/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs b/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs index 0f4783d581b..ffff8a02869 100644 --- a/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs +++ b/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs @@ -79,6 +79,10 @@ pub fn core_libfunc_ap_change( | ArrayConcreteLibfunc::SnapshotPopBack(_) => { vec![ApChange::Known(1), ApChange::Known(1)] } + ArrayConcreteLibfunc::SnapshotMultiPopFront(_) + | ArrayConcreteLibfunc::SnapshotMultiPopBack(_) => { + vec![ApChange::Known(3), ApChange::Known(3)] + } ArrayConcreteLibfunc::Get(libfunc) => { if info_provider.type_size(&libfunc.ty) == 1 { [4, 3] } else { [5, 4] } .map(ApChange::Known) diff --git a/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs b/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs index badd51211e0..2e1c059bc7d 100644 --- a/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs +++ b/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs @@ -258,6 +258,13 @@ pub fn core_libfunc_cost( | ArrayConcreteLibfunc::SnapshotPopBack(_) => { vec![ConstCost::steps(2).into(), ConstCost::steps(3).into()] } + ArrayConcreteLibfunc::SnapshotMultiPopFront(_) + | ArrayConcreteLibfunc::SnapshotMultiPopBack(_) => { + vec![ + (ConstCost::steps(4) + ConstCost::range_checks(1)).into(), + (ConstCost::steps(5) + ConstCost::range_checks(1)).into(), + ] + } ArrayConcreteLibfunc::Get(libfunc) => { if info_provider.type_size(&libfunc.ty) == 1 { vec![ diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/array.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/array.rs index 492bbed79d5..9c615ae815d 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/array.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/array.rs @@ -1,6 +1,6 @@ -use cairo_lang_casm::builder::CasmBuilder; +use cairo_lang_casm::builder::{CasmBuilder, Var}; use cairo_lang_casm::casm_build_extend; -use cairo_lang_sierra::extensions::array::ArrayConcreteLibfunc; +use cairo_lang_sierra::extensions::array::{ArrayConcreteLibfunc, ConcreteMultiPopLibfunc}; use cairo_lang_sierra::ids::ConcreteTypeId; use super::{CompiledInvocation, CompiledInvocationBuilder, InvocationError}; @@ -28,6 +28,12 @@ pub fn build( ArrayConcreteLibfunc::SnapshotPopBack(libfunc) => { build_pop_back(&libfunc.ty, builder, false) } + ArrayConcreteLibfunc::SnapshotMultiPopFront(libfunc) => { + build_multi_pop_front(libfunc, builder) + } + ArrayConcreteLibfunc::SnapshotMultiPopBack(libfunc) => { + build_multi_pop_back(libfunc, builder) + } ArrayConcreteLibfunc::Get(libfunc) => build_array_get(&libfunc.ty, builder), ArrayConcreteLibfunc::Slice(libfunc) => build_array_slice(&libfunc.ty, builder), ArrayConcreteLibfunc::Len(libfunc) => build_array_len(&libfunc.ty, builder), @@ -365,3 +371,124 @@ fn build_array_len( Default::default(), )) } + +/// Handles a Sierra statement for popping elements from the beginning of an array. +fn build_multi_pop_front( + libfunc: &ConcreteMultiPopLibfunc, + builder: CompiledInvocationBuilder<'_>, +) -> Result { + let [expr_range_check, expr_arr] = builder.try_get_refs()?; + let range_check = expr_range_check.try_unpack_single()?; + let [arr_start, arr_end] = expr_arr.try_unpack()?; + let element_size = builder.program_info.type_sizes[&libfunc.ty]; + + let mut casm_builder = CasmBuilder::default(); + add_input_variables! {casm_builder, + buffer(1) range_check; + deref arr_start; + deref arr_end; + }; + let popped_size: i16 = libfunc.popped_values * element_size; + casm_build_extend!(casm_builder, let orig_range_check = range_check;); + extend_multi_pop_failure_checks( + &mut casm_builder, + range_check, + arr_start, + arr_end, + popped_size, + ); + casm_build_extend! {casm_builder, + // Success case. + const popped_size = popped_size; + tempvar rc; + // Calculating the new start, as it is required for calculating the range checked value. + tempvar new_start = arr_start + popped_size; + assert rc = arr_end - new_start; + assert rc = *(range_check++); + }; + let failure_handle = get_non_fallthrough_statement_id(&builder); + Ok(builder.build_from_casm_builder( + casm_builder, + [ + ("Fallthrough", &[&[range_check], &[new_start, arr_end], &[arr_start]], None), + ("Failure", &[&[range_check], &[arr_start, arr_end]], Some(failure_handle)), + ], + CostValidationInfo { + range_check_info: Some((orig_range_check, range_check)), + extra_costs: None, + }, + )) +} + +/// Handles a Sierra statement for popping elements from the end of an array. +fn build_multi_pop_back( + libfunc: &ConcreteMultiPopLibfunc, + builder: CompiledInvocationBuilder<'_>, +) -> Result { + let [expr_range_check, expr_arr] = builder.try_get_refs()?; + let range_check = expr_range_check.try_unpack_single()?; + let [arr_start, arr_end] = expr_arr.try_unpack()?; + let element_size = builder.program_info.type_sizes[&libfunc.ty]; + + let mut casm_builder = CasmBuilder::default(); + add_input_variables! {casm_builder, + buffer(1) range_check; + deref arr_start; + deref arr_end; + }; + let popped_size: i16 = libfunc.popped_values * element_size; + casm_build_extend!(casm_builder, let orig_range_check = range_check;); + extend_multi_pop_failure_checks( + &mut casm_builder, + range_check, + arr_start, + arr_end, + popped_size, + ); + casm_build_extend! {casm_builder, + // Success case. + const popped_size = popped_size; + tempvar rc; + // Calculating the new end, as it is required for calculating the range checked value. + tempvar new_end = arr_end - popped_size; + assert rc = new_end - arr_start; + assert rc = *(range_check++); + }; + let failure_handle = get_non_fallthrough_statement_id(&builder); + Ok(builder.build_from_casm_builder( + casm_builder, + [ + ("Fallthrough", &[&[range_check], &[arr_start, new_end], &[new_end]], None), + ("Failure", &[&[range_check], &[arr_start, arr_end]], Some(failure_handle)), + ], + CostValidationInfo { + range_check_info: Some((orig_range_check, range_check)), + extra_costs: None, + }, + )) +} + +/// Extends the casm builder with the common part of the multi-pop front and multi-pop back. +fn extend_multi_pop_failure_checks( + casm_builder: &mut CasmBuilder, + range_check: Var, + arr_start: Var, + arr_end: Var, + popped_size: i16, +) { + casm_build_extend! {casm_builder, + const popped_size_plus_1 = popped_size + 1; + const popped_size = popped_size; + let arr_start_popped = arr_start + popped_size; + tempvar has_enough_elements; + hint TestLessThanOrEqual {lhs: arr_start_popped, rhs: arr_end} into {dst: has_enough_elements}; + jump HasEnoughElements if has_enough_elements != 0; + // Proving that `arr_start - arr_end + popped_size + 1 >= 0` and therefore + // `popped_size + 1 >= arr_end - arr_end == arr.len()` ==> `arr.len() < popopped_size`. + tempvar minus_length = arr_start - arr_end; + tempvar rc = minus_length + popped_size_plus_1; + assert rc = *(range_check++); + jump Failure; + HasEnoughElements: + }; +} diff --git a/crates/cairo-lang-sierra/src/extensions/mod.rs b/crates/cairo-lang-sierra/src/extensions/mod.rs index b05468c930b..1d17eda765b 100644 --- a/crates/cairo-lang-sierra/src/extensions/mod.rs +++ b/crates/cairo-lang-sierra/src/extensions/mod.rs @@ -51,6 +51,17 @@ fn args_as_two_types( } } +/// Helper for extracting a type and a value from the template arguments. +fn args_as_type_and_value( + args: &[GenericArg], +) -> Result<(ConcreteTypeId, BigInt), SpecializationError> { + match args { + [GenericArg::Type(ty), GenericArg::Value(value)] => Ok((ty.clone(), value.clone())), + [_, _] => Err(SpecializationError::UnsupportedGenericArg), + _ => Err(SpecializationError::WrongNumberOfGenericArgs), + } +} + /// Helper for extracting the type from the template arguments. fn args_as_single_user_func(args: &[GenericArg]) -> Result { match args { diff --git a/crates/cairo-lang-sierra/src/extensions/modules/array.rs b/crates/cairo-lang-sierra/src/extensions/modules/array.rs index 8b7f6d1e352..fce6170c894 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/array.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/array.rs @@ -1,19 +1,23 @@ +use num_traits::ToPrimitive; + use super::boxing::box_ty; use super::range_check::RangeCheckType; use super::snapshot::snapshot_ty; use super::structure::StructConcreteType; +use super::utils::fixed_size_array_ty; use crate::define_libfunc_hierarchy; use crate::extensions::lib_func::{ BranchSignature, DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange, SignatureAndTypeGenericLibfunc, SignatureOnlyGenericLibfunc, - SignatureSpecializationContext, WrapSignatureAndTypeGenericLibfunc, + SignatureSpecializationContext, SpecializationContext, WrapSignatureAndTypeGenericLibfunc, }; use crate::extensions::type_specialization_context::TypeSpecializationContext; use crate::extensions::types::{ GenericTypeArgGenericType, GenericTypeArgGenericTypeWrapper, TypeInfo, }; use crate::extensions::{ - args_as_single_type, NamedType, OutputVarReferenceInfo, SpecializationError, + args_as_single_type, args_as_type_and_value, NamedLibfunc, NamedType, OutputVarReferenceInfo, + SignatureBasedConcreteLibfunc, SpecializationError, }; use crate::ids::{ConcreteTypeId, GenericTypeId}; use crate::program::GenericArg; @@ -60,6 +64,8 @@ define_libfunc_hierarchy! { Len(ArrayLenLibfunc), SnapshotPopFront(ArraySnapshotPopFrontLibfunc), SnapshotPopBack(ArraySnapshotPopBackLibfunc), + SnapshotMultiPopFront(ArraySnapshotMultiPopFrontLibfunc), + SnapshotMultiPopBack(ArraySnapshotMultiPopBackLibfunc), }, ArrayConcreteLibfunc } @@ -502,3 +508,177 @@ impl SignatureAndTypeGenericLibfunc for ArraySnapshotPopBackLibfuncWrapped { } pub type ArraySnapshotPopBackLibfunc = WrapSignatureAndTypeGenericLibfunc; + +/// Libfunc for popping multiple first values from the beginning of an array snapshot. +#[derive(Default)] +pub struct ArraySnapshotMultiPopFrontLibfunc {} +impl NamedLibfunc for ArraySnapshotMultiPopFrontLibfunc { + const STR_ID: &'static str = "array_snapshot_multi_pop_front"; + + type Concrete = ConcreteMultiPopLibfunc; + + fn specialize_signature( + &self, + context: &dyn SignatureSpecializationContext, + args: &[GenericArg], + ) -> Result { + let (ty, count) = args_as_type_and_value(args)?; + let arr_ty = context.get_wrapped_concrete_type(ArrayType::id(), ty.clone())?; + let arr_snapshot_ty = snapshot_ty(context, arr_ty)?; + let range_check_ty = context.get_concrete_type(RangeCheckType::id(), &[])?; + Ok(LibfuncSignature { + param_signatures: vec![ + ParamSignature::new(range_check_ty.clone()).with_allow_add_const(), + ParamSignature::new(arr_snapshot_ty.clone()), + ], + branch_signatures: vec![ + BranchSignature { + vars: vec![ + OutputVarInfo::new_builtin(range_check_ty.clone(), 0), + OutputVarInfo { + ty: arr_snapshot_ty.clone(), + ref_info: OutputVarReferenceInfo::Deferred( + DeferredOutputKind::AddConst { param_idx: 1 }, + ), + }, + OutputVarInfo { + ty: snapshot_ty( + context, + box_ty( + context, + fixed_size_array_ty( + context, + ty, + count + .to_i16() + .ok_or(SpecializationError::UnsupportedGenericArg)?, + )?, + )?, + )?, + ref_info: OutputVarReferenceInfo::PartialParam { param_idx: 1 }, + }, + ], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + BranchSignature { + vars: vec![ + OutputVarInfo::new_builtin(range_check_ty, 0), + OutputVarInfo { + ty: arr_snapshot_ty, + ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 1 }, + }, + ], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + ], + fallthrough: Some(0), + }) + } + + fn specialize( + &self, + context: &dyn SpecializationContext, + args: &[GenericArg], + ) -> Result { + let (ty, count) = args_as_type_and_value(args)?; + Ok(ConcreteMultiPopLibfunc { + ty, + signature: self.specialize_signature(context.upcast(), args)?, + // Early failing on size, although the type itself would fail on definition. + popped_values: count.to_i16().ok_or(SpecializationError::UnsupportedGenericArg)?, + }) + } +} + +/// Libfunc for popping the last value from the end of an array snapshot. +#[derive(Default)] +pub struct ArraySnapshotMultiPopBackLibfunc {} +impl NamedLibfunc for ArraySnapshotMultiPopBackLibfunc { + const STR_ID: &'static str = "array_snapshot_multi_pop_back"; + + type Concrete = ConcreteMultiPopLibfunc; + + fn specialize_signature( + &self, + context: &dyn SignatureSpecializationContext, + args: &[GenericArg], + ) -> Result { + let (ty, count) = args_as_type_and_value(args)?; + let arr_ty = context.get_wrapped_concrete_type(ArrayType::id(), ty.clone())?; + let arr_snapshot_ty = snapshot_ty(context, arr_ty)?; + let range_check_ty = context.get_concrete_type(RangeCheckType::id(), &[])?; + Ok(LibfuncSignature { + param_signatures: vec![ + ParamSignature::new(range_check_ty.clone()).with_allow_add_const(), + ParamSignature::new(arr_snapshot_ty.clone()), + ], + branch_signatures: vec![ + BranchSignature { + vars: vec![ + OutputVarInfo::new_builtin(range_check_ty.clone(), 0), + OutputVarInfo { + ty: arr_snapshot_ty.clone(), + ref_info: OutputVarReferenceInfo::Deferred( + DeferredOutputKind::AddConst { param_idx: 1 }, + ), + }, + OutputVarInfo { + ty: snapshot_ty( + context, + box_ty( + context, + fixed_size_array_ty( + context, + ty, + count + .to_i16() + .ok_or(SpecializationError::UnsupportedGenericArg)?, + )?, + )?, + )?, + ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic), + }, + ], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + BranchSignature { + vars: vec![ + OutputVarInfo::new_builtin(range_check_ty, 0), + OutputVarInfo { + ty: arr_snapshot_ty, + ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 1 }, + }, + ], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + ], + fallthrough: Some(0), + }) + } + + fn specialize( + &self, + context: &dyn SpecializationContext, + args: &[GenericArg], + ) -> Result { + let (ty, count) = args_as_type_and_value(args)?; + Ok(ConcreteMultiPopLibfunc { + ty, + signature: self.specialize_signature(context.upcast(), args)?, + // Early failing on size, although the type itself would fail on definition. + popped_values: count.to_i16().ok_or(SpecializationError::UnsupportedGenericArg)?, + }) + } +} + +/// Struct the data for a multi pop action. +pub struct ConcreteMultiPopLibfunc { + pub ty: ConcreteTypeId, + pub popped_values: i16, + pub signature: LibfuncSignature, +} +impl SignatureBasedConcreteLibfunc for ConcreteMultiPopLibfunc { + fn signature(&self) -> &LibfuncSignature { + &self.signature + } +} diff --git a/crates/cairo-lang-sierra/src/extensions/modules/starknet/syscalls.rs b/crates/cairo-lang-sierra/src/extensions/modules/starknet/syscalls.rs index bd52f006f61..fc956b9d991 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/starknet/syscalls.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/starknet/syscalls.rs @@ -1,4 +1,4 @@ -use itertools::{chain, repeat_n}; +use itertools::chain; use super::interoperability::ClassHashType; use super::u64_span_ty; @@ -12,14 +12,12 @@ use crate::extensions::lib_func::{ SierraApChange, SignatureSpecializationContext, }; use crate::extensions::modules::get_u256_type; -use crate::extensions::structure::StructType; -use crate::extensions::utils::reinterpret_cast_signature; +use crate::extensions::utils::{fixed_size_array_ty, reinterpret_cast_signature}; use crate::extensions::{ NamedType, NoGenericArgsGenericLibfunc, NoGenericArgsGenericType, OutputVarReferenceInfo, SpecializationError, }; -use crate::ids::{ConcreteTypeId, GenericTypeId, UserTypeId}; -use crate::program::GenericArg; +use crate::ids::{ConcreteTypeId, GenericTypeId}; /// Type for Starknet system object. /// Used to make system calls. @@ -243,12 +241,8 @@ pub fn sha256_state_handle_unwrapped_type( /// Returns a fixed type array of the given type and size. fn boxed_u32_fixed_array_ty( context: &dyn SignatureSpecializationContext, - size: usize, + size: i16, ) -> Result { - let args: Vec = chain!( - [GenericArg::UserType(UserTypeId::from_string("Tuple"))], - repeat_n(GenericArg::Type(context.get_concrete_type(Uint32Type::id(), &[])?), size) - ) - .collect(); - box_ty(context, context.get_concrete_type(StructType::id(), &args)?) + let ty = context.get_concrete_type(Uint32Type::id(), &[])?; + box_ty(context, fixed_size_array_ty(context, ty, size)?) } diff --git a/crates/cairo-lang-sierra/src/extensions/modules/utils.rs b/crates/cairo-lang-sierra/src/extensions/modules/utils.rs index 887167fa8ac..0a5a8fb9bdd 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/utils.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/utils.rs @@ -1,6 +1,8 @@ use std::ops::Shl; use cairo_felt::Felt252; +use cairo_lang_utils::casts::IntoOrPanic; +use itertools::{chain, repeat_n}; use num_bigint::BigInt; use num_traits::One; @@ -11,12 +13,13 @@ use super::int::signed::{Sint16Type, Sint32Type, Sint64Type, Sint8Type}; use super::int::signed128::Sint128Type; use super::int::unsigned::{Uint16Type, Uint32Type, Uint64Type, Uint8Type}; use super::int::unsigned128::Uint128Type; +use super::structure::StructType; use crate::extensions::lib_func::{ LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange, SignatureSpecializationContext, }; use crate::extensions::types::TypeInfo; use crate::extensions::{NamedType, OutputVarReferenceInfo, SpecializationError}; -use crate::ids::ConcreteTypeId; +use crate::ids::{ConcreteTypeId, UserTypeId}; use crate::program::GenericArg; /// Returns a libfunc signature that casts from one type to another, without changing the internal @@ -109,3 +112,17 @@ impl Range { if lower < upper { Some(Self::half_open(lower, upper)) } else { None } } } + +/// Returns a fixed type array of the given type and size. +pub fn fixed_size_array_ty( + context: &dyn SignatureSpecializationContext, + ty: ConcreteTypeId, + size: i16, +) -> Result { + let args: Vec = chain!( + [GenericArg::UserType(UserTypeId::from_string("Tuple"))], + repeat_n(GenericArg::Type(ty), size.into_or_panic()) + ) + .collect(); + context.get_concrete_type(StructType::id(), &args) +} diff --git a/crates/cairo-lang-sierra/src/simulation/core.rs b/crates/cairo-lang-sierra/src/simulation/core.rs index 4fecdd96b06..adecc95c1c9 100644 --- a/crates/cairo-lang-sierra/src/simulation/core.rs +++ b/crates/cairo-lang-sierra/src/simulation/core.rs @@ -225,6 +225,8 @@ pub fn simulate< }, Array(ArrayConcreteLibfunc::SnapshotPopFront(_)) => todo!(), Array(ArrayConcreteLibfunc::SnapshotPopBack(_)) => todo!(), + Array(ArrayConcreteLibfunc::SnapshotMultiPopFront(_)) => todo!(), + Array(ArrayConcreteLibfunc::SnapshotMultiPopBack(_)) => todo!(), Uint8(libfunc) => simulate_u8_libfunc(libfunc, &inputs), Uint16(libfunc) => simulate_u16_libfunc(libfunc, &inputs), Uint32(libfunc) => simulate_u32_libfunc(libfunc, &inputs), diff --git a/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json b/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json index 159d35b5a7d..927b4e9b5b0 100644 --- a/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json +++ b/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json @@ -8,6 +8,8 @@ "array_pop_front", "array_pop_front_consume", "array_slice", + "array_snapshot_multi_pop_back", + "array_snapshot_multi_pop_front", "array_snapshot_pop_back", "array_snapshot_pop_front", "bitwise", diff --git a/tests/e2e_test_data/libfuncs/array b/tests/e2e_test_data/libfuncs/array index 480a3f6f663..3d1c6d2b461 100644 --- a/tests/e2e_test_data/libfuncs/array +++ b/tests/e2e_test_data/libfuncs/array @@ -947,3 +947,153 @@ store_temp>) -> (core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>); + +//! > ========================================================================== + +//! > array_snapshot_multi_pop_front libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +extern fn array_snapshot_multi_pop_front(ref arr: @Array::) -> Option<@Box<[T; SIZE]>> implicits(RangeCheck) nopanic; +fn foo(ref arr: @Array::) -> Option::<@Box<[felt252; 2]>> { + array_snapshot_multi_pop_front(ref arr) +} + +//! > casm +%{ memory[ap + 0] = (memory[fp + -4] + 2) % PRIME <= memory[fp + -3] %} +jmp rel 8 if [ap + 0] != 0, ap++; +[fp + -4] = [ap + 0] + [fp + -3], ap++; +[ap + 0] = [ap + -1] + 3, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 14; +[ap + 1] = [fp + -4] + 2, ap++; +[fp + -3] = [ap + -1] + [ap + 0], ap++; +[ap + -2] = [[fp + -5] + 0]; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = [ap + -2], ap++; +[ap + 0] = [fp + -3], ap++; +[ap + 0] = 0, ap++; +[ap + 0] = [fp + -4], ap++; +ret; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = [fp + -4], ap++; +[ap + 0] = [fp + -3], ap++; +[ap + 0] = 1, ap++; +[ap + 0] = 0, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1070}) + +//! > sierra_code +type RangeCheck = RangeCheck [storable: true, drop: false, dup: false, zero_sized: false]; +type Unit = Struct [storable: true, drop: true, dup: true, zero_sized: true]; +type Box> = Box> [storable: true, drop: true, dup: true, zero_sized: false]; +type core::option::Option::<@core::box::Box::<[core::felt252; 2]>> = Enum>, Box>, Unit> [storable: true, drop: true, dup: true, zero_sized: false]; +type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false]; +type Tuple = Struct [storable: true, drop: true, dup: true, zero_sized: false]; +type Array = Array [storable: true, drop: true, dup: false, zero_sized: false]; +type Snapshot> = Snapshot> [storable: true, drop: true, dup: true, zero_sized: false]; + +libfunc array_snapshot_multi_pop_front = array_snapshot_multi_pop_front; +libfunc branch_align = branch_align; +libfunc enum_init>, 0> = enum_init>, 0>; +libfunc store_temp = store_temp; +libfunc store_temp>> = store_temp>>; +libfunc store_temp>> = store_temp>>; +libfunc struct_construct = struct_construct; +libfunc enum_init>, 1> = enum_init>, 1>; + +array_snapshot_multi_pop_front([0], [1]) { fallthrough([2], [3], [4]) 7([5], [6]) }; // 0 +branch_align() -> (); // 1 +enum_init>, 0>([4]) -> ([7]); // 2 +store_temp([2]) -> ([2]); // 3 +store_temp>>([3]) -> ([3]); // 4 +store_temp>>([7]) -> ([7]); // 5 +return([2], [3], [7]); // 6 +branch_align() -> (); // 7 +struct_construct() -> ([8]); // 8 +enum_init>, 1>([8]) -> ([9]); // 9 +store_temp([5]) -> ([5]); // 10 +store_temp>>([6]) -> ([6]); // 11 +store_temp>>([9]) -> ([9]); // 12 +return([5], [6], [9]); // 13 + +test::foo@0([0]: RangeCheck, [1]: Snapshot>) -> (RangeCheck, Snapshot>, core::option::Option::<@core::box::Box::<[core::felt252; 2]>>); + +//! > ========================================================================== + +//! > array_snapshot_multi_pop_back libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +extern fn array_snapshot_multi_pop_back(ref arr: @Array::) -> Option<@Box<[T; SIZE]>> implicits(RangeCheck) nopanic; +fn foo(ref arr: @Array::) -> Option::<@Box<[felt252; 2]>> { + array_snapshot_multi_pop_back(ref arr) +} + +//! > casm +%{ memory[ap + 0] = (memory[fp + -4] + 2) % PRIME <= memory[fp + -3] %} +jmp rel 8 if [ap + 0] != 0, ap++; +[fp + -4] = [ap + 0] + [fp + -3], ap++; +[ap + 0] = [ap + -1] + 3, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 14; +[fp + -3] = [ap + 1] + 2, ap++; +[ap + 0] = [ap + -1] + [fp + -4], ap++; +[ap + -2] = [[fp + -5] + 0]; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = [fp + -4], ap++; +[ap + 0] = [ap + -3], ap++; +[ap + 0] = 0, ap++; +[ap + 0] = [ap + -5], ap++; +ret; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = [fp + -4], ap++; +[ap + 0] = [fp + -3], ap++; +[ap + 0] = 1, ap++; +[ap + 0] = 0, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1070}) + +//! > sierra_code +type RangeCheck = RangeCheck [storable: true, drop: false, dup: false, zero_sized: false]; +type Unit = Struct [storable: true, drop: true, dup: true, zero_sized: true]; +type Box> = Box> [storable: true, drop: true, dup: true, zero_sized: false]; +type core::option::Option::<@core::box::Box::<[core::felt252; 2]>> = Enum>, Box>, Unit> [storable: true, drop: true, dup: true, zero_sized: false]; +type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false]; +type Tuple = Struct [storable: true, drop: true, dup: true, zero_sized: false]; +type Array = Array [storable: true, drop: true, dup: false, zero_sized: false]; +type Snapshot> = Snapshot> [storable: true, drop: true, dup: true, zero_sized: false]; + +libfunc array_snapshot_multi_pop_back = array_snapshot_multi_pop_back; +libfunc branch_align = branch_align; +libfunc enum_init>, 0> = enum_init>, 0>; +libfunc store_temp = store_temp; +libfunc store_temp>> = store_temp>>; +libfunc store_temp>> = store_temp>>; +libfunc struct_construct = struct_construct; +libfunc enum_init>, 1> = enum_init>, 1>; + +array_snapshot_multi_pop_back([0], [1]) { fallthrough([2], [3], [4]) 7([5], [6]) }; // 0 +branch_align() -> (); // 1 +enum_init>, 0>([4]) -> ([7]); // 2 +store_temp([2]) -> ([2]); // 3 +store_temp>>([3]) -> ([3]); // 4 +store_temp>>([7]) -> ([7]); // 5 +return([2], [3], [7]); // 6 +branch_align() -> (); // 7 +struct_construct() -> ([8]); // 8 +enum_init>, 1>([8]) -> ([9]); // 9 +store_temp([5]) -> ([5]); // 10 +store_temp>>([6]) -> ([6]); // 11 +store_temp>>([9]) -> ([9]); // 12 +return([5], [6], [9]); // 13 + +test::foo@0([0]: RangeCheck, [1]: Snapshot>) -> (RangeCheck, Snapshot>, core::option::Option::<@core::box::Box::<[core::felt252; 2]>>);