From ce3e4a7bb81a3280b2b914d5f59d66619d2c2aa9 Mon Sep 17 00:00:00 2001 From: ilyalesokhin-starkware Date: Tue, 30 Apr 2024 11:23:53 +0300 Subject: [PATCH] Add init_circuit_data. (#5481) --- corelib/src/circuit.cairo | 15 +- .../src/core_libfunc_ap_change.rs | 2 + .../src/core_libfunc_cost_base.rs | 2 + .../src/invocations/circuit.rs | 46 ++++ .../src/invocations/mod.rs | 2 + crates/cairo-lang-sierra-type-size/src/lib.rs | 12 +- .../cairo-lang-sierra/src/extensions/core.rs | 3 + .../cairo-lang-sierra/src/extensions/mod.rs | 14 ++ .../src/extensions/modules/circuit.rs | 206 ++++++++++++++++++ .../src/extensions/modules/const_type.rs | 17 +- .../src/extensions/modules/mod.rs | 1 + .../src/extensions/modules/structure.rs | 5 +- .../cairo-lang-sierra/src/extensions/test.rs | 2 +- .../cairo-lang-sierra/src/simulation/core.rs | 1 + .../src/allowed_libfuncs_lists/all.json | 1 + tests/e2e_test.rs | 1 + tests/e2e_test_data/libfuncs/circuit | 38 ++++ 17 files changed, 347 insertions(+), 21 deletions(-) create mode 100644 crates/cairo-lang-sierra-to-casm/src/invocations/circuit.rs create mode 100644 crates/cairo-lang-sierra/src/extensions/modules/circuit.rs create mode 100644 tests/e2e_test_data/libfuncs/circuit diff --git a/corelib/src/circuit.cairo b/corelib/src/circuit.cairo index f3e6627c973..e930e3358cb 100644 --- a/corelib/src/circuit.cairo +++ b/corelib/src/circuit.cairo @@ -1,3 +1,16 @@ pub type u96 = core::internal::BoundedInt<0, 79228162514264337593543950335>; - pub extern type RangeCheck96; + + +// Defines an input for a circuit. +#[phantom] +pub extern type CircuitInput; + + +// Initializes the input data for running an instance of the circuit. +extern fn init_circuit_data() -> CircuitInputAccumulator implicits(RangeCheck96) nopanic; + +// Type for accumulating inputs into the circuit instance's data. +extern type CircuitInputAccumulator; + +impl CircuitInputAccumulatorDrop of Drop>; 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 70fea87763f..04f1b08c21e 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 @@ -9,6 +9,7 @@ use cairo_lang_sierra::extensions::bounded_int::{ use cairo_lang_sierra::extensions::boxing::BoxConcreteLibfunc; use cairo_lang_sierra::extensions::bytes31::Bytes31ConcreteLibfunc; use cairo_lang_sierra::extensions::casts::{CastConcreteLibfunc, CastType}; +use cairo_lang_sierra::extensions::circuit::CircuitConcreteLibfunc; use cairo_lang_sierra::extensions::const_type::ConstConcreteLibfunc; use cairo_lang_sierra::extensions::core::CoreConcreteLibfunc::{self, *}; use cairo_lang_sierra::extensions::coupon::CouponConcreteLibfunc; @@ -380,6 +381,7 @@ pub fn core_libfunc_ap_change( ] } }, + Circuit(CircuitConcreteLibfunc::InitCircuitData(_)) => vec![ApChange::Known(0)], } } 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 d7b2ed07b8f..57d8fadae11 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 @@ -9,6 +9,7 @@ use cairo_lang_sierra::extensions::bounded_int::{ use cairo_lang_sierra::extensions::boxing::BoxConcreteLibfunc; use cairo_lang_sierra::extensions::bytes31::Bytes31ConcreteLibfunc; use cairo_lang_sierra::extensions::casts::{CastConcreteLibfunc, CastType}; +use cairo_lang_sierra::extensions::circuit::CircuitConcreteLibfunc; use cairo_lang_sierra::extensions::const_type::ConstConcreteLibfunc; use cairo_lang_sierra::extensions::core::CoreConcreteLibfunc::{self, *}; use cairo_lang_sierra::extensions::coupon::CouponConcreteLibfunc; @@ -470,6 +471,7 @@ pub fn core_libfunc_cost( ] } }, + Circuit(CircuitConcreteLibfunc::InitCircuitData(_)) => vec![ConstCost::steps(0).into()], } } diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/circuit.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/circuit.rs new file mode 100644 index 00000000000..e0605fcd961 --- /dev/null +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/circuit.rs @@ -0,0 +1,46 @@ +use cairo_lang_casm::builder::CasmBuilder; +use cairo_lang_casm::casm_build_extend; +use cairo_lang_sierra::extensions::circuit::CircuitConcreteLibfunc; + +use super::{CompiledInvocation, CompiledInvocationBuilder, InvocationError}; +use crate::invocations::add_input_variables; + +/// Builds instructions for Sierra array operations. +pub fn build( + libfunc: &CircuitConcreteLibfunc, + builder: CompiledInvocationBuilder<'_>, +) -> Result { + match libfunc { + CircuitConcreteLibfunc::InitCircuitData(_) => build_init_circuit_data(builder), + } +} + +/// Handles a Sierra statement for initializing circuit data. +fn build_init_circuit_data( + builder: CompiledInvocationBuilder<'_>, +) -> Result { + let [expr_rc96] = builder.try_get_refs()?; + let rc96 = expr_rc96.try_unpack_single()?; + + // TODO(ilya): get n_inputs and n_vals from the libfunc. + let n_inputs = 1; + let n_vals = 2; + + let mut casm_builder = CasmBuilder::default(); + + add_input_variables! {casm_builder, + buffer(1) rc96; + }; + casm_build_extend! {casm_builder, + const n_inputs = n_inputs; + let inputs_end = rc96 + n_inputs; + const n_vals = n_vals; + let vals_end = rc96 + n_vals; + }; + + Ok(builder.build_from_casm_builder( + casm_builder, + [("Fallthrough", &[&[vals_end], &[rc96, inputs_end]], None)], + Default::default(), + )) +} diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/mod.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/mod.rs index 6d30f7d9e4e..210cddc1b07 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/mod.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/mod.rs @@ -38,6 +38,7 @@ mod boolean; mod boxing; mod bytes31; mod casts; +mod circuit; mod const_type; mod debug; mod ec; @@ -689,6 +690,7 @@ pub fn compile_invocation( } }, BoundedInt(libfunc) => int::bounded::build(libfunc, builder), + Circuit(libfunc) => circuit::build(libfunc, builder), } } diff --git a/crates/cairo-lang-sierra-type-size/src/lib.rs b/crates/cairo-lang-sierra-type-size/src/lib.rs index e39875c23a2..24fed07907a 100644 --- a/crates/cairo-lang-sierra-type-size/src/lib.rs +++ b/crates/cairo-lang-sierra-type-size/src/lib.rs @@ -1,3 +1,4 @@ +use cairo_lang_sierra::extensions::circuit::CircuitTypeConcrete; use cairo_lang_sierra::extensions::core::{CoreLibfunc, CoreType, CoreTypeConcrete}; use cairo_lang_sierra::extensions::starknet::StarkNetTypeConcrete; use cairo_lang_sierra::ids::ConcreteTypeId; @@ -68,14 +69,21 @@ pub fn get_type_size_map( Some(size) } CoreTypeConcrete::Struct(struct_type) => { + if !struct_type.info.storable { + // If the struct is not storable, it should not have a size. + continue; + } let mut size = 0; for member in &struct_type.members { size += type_sizes.get(member).cloned()?; } Some(size) } - // Const types are not moved around and should not have a size. - CoreTypeConcrete::Const(_) => continue, + + CoreTypeConcrete::Circuit(CircuitTypeConcrete::CircuitInputAccumulator(_)) => Some(2), + + // Const and circuit types are not moved around and should not have a size. + CoreTypeConcrete::Const(_) | CoreTypeConcrete::Circuit(_) => continue, }?; type_sizes.insert(declaration.id.clone(), size); } diff --git a/crates/cairo-lang-sierra/src/extensions/core.rs b/crates/cairo-lang-sierra/src/extensions/core.rs index ea58c959a82..bda63468f33 100644 --- a/crates/cairo-lang-sierra/src/extensions/core.rs +++ b/crates/cairo-lang-sierra/src/extensions/core.rs @@ -6,6 +6,7 @@ use super::bounded_int::{BoundedIntLibfunc, BoundedIntType}; use super::branch_align::BranchAlignLibfunc; use super::bytes31::{Bytes31Libfunc, Bytes31Type}; use super::casts::CastLibfunc; +use super::circuit::{CircuitLibFunc, CircuitType}; use super::const_type::{ConstLibfunc, ConstType}; use super::coupon::{CouponLibfunc, CouponType}; use super::debug::DebugLibfunc; @@ -56,6 +57,7 @@ define_type_hierarchy! { Coupon(CouponType), Bitwise(BitwiseType), Box(BoxType), + Circuit(CircuitType), Const(ConstType), EcOp(EcOpType), EcPoint(EcPointType), @@ -103,6 +105,7 @@ define_libfunc_hierarchy! { Bool(BoolLibfunc), Box(BoxLibfunc), Cast(CastLibfunc), + Circuit(CircuitLibFunc), Coupon(CouponLibfunc), CouponCall(CouponCallLibfunc), Drop(DropLibfunc), diff --git a/crates/cairo-lang-sierra/src/extensions/mod.rs b/crates/cairo-lang-sierra/src/extensions/mod.rs index 7c689946c1f..a70c0164897 100644 --- a/crates/cairo-lang-sierra/src/extensions/mod.rs +++ b/crates/cairo-lang-sierra/src/extensions/mod.rs @@ -15,6 +15,7 @@ pub use self::lib_func::{ OutputVarReferenceInfo, SignatureBasedConcreteLibfunc, }; pub use self::modules::*; +use self::type_specialization_context::TypeSpecializationContext; pub use self::types::{ ConcreteType, GenericType, GenericTypeEx, NamedType, NoGenericArgsGenericType, }; @@ -59,5 +60,18 @@ fn args_as_single_user_func(args: &[GenericArg]) -> Result( + context: &dyn TypeSpecializationContext, + ty: &ConcreteTypeId, +) -> Result, SpecializationError> { + let long_id = context.get_type_info(ty.clone())?.long_id; + if long_id.generic_id != T::ID { + Err(SpecializationError::UnsupportedGenericArg) + } else { + Ok(long_id.generic_args) + } +} + #[cfg(test)] mod test; diff --git a/crates/cairo-lang-sierra/src/extensions/modules/circuit.rs b/crates/cairo-lang-sierra/src/extensions/modules/circuit.rs new file mode 100644 index 00000000000..7fb2d70069c --- /dev/null +++ b/crates/cairo-lang-sierra/src/extensions/modules/circuit.rs @@ -0,0 +1,206 @@ +use num_bigint::BigInt; + +use super::range_check::RangeCheck96Type; +use super::structure::StructType; +use crate::extensions::lib_func::{ + DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange, + SignatureOnlyGenericLibfunc, SignatureSpecializationContext, +}; +use crate::extensions::type_specialization_context::TypeSpecializationContext; +use crate::extensions::types::TypeInfo; +use crate::extensions::{ + args_as_single_type, args_as_single_value, extract_type_generic_args, ConcreteType, NamedType, + OutputVarReferenceInfo, SpecializationError, +}; +use crate::ids::{ConcreteTypeId, GenericTypeId, UserTypeId}; +use crate::program::{ConcreteTypeLongId, GenericArg}; +use crate::{define_libfunc_hierarchy, define_type_hierarchy}; + +define_type_hierarchy! { + pub enum CircuitType { + CircuitInput(CircuitInput), + CircuitInputAccumulator(CircuitInputAccumulator), + }, CircuitTypeConcrete +} + +define_libfunc_hierarchy! { + pub enum CircuitLibFunc { + InitCircuitData(InitCircuitData), + }, CircuitConcreteLibfunc +} + +/// Returns true if `garg` is a type that is considered a circuit component. +fn is_circuit_component( + context: &dyn TypeSpecializationContext, + garg: &GenericArg, +) -> Result { + let GenericArg::Type(ty) = garg else { + return Err(SpecializationError::UnsupportedGenericArg); + }; + + let long_id = context.get_type_info(ty.clone())?.long_id; + let generic_id = long_id.generic_id; + if generic_id == CircuitInput::ID { + return Ok(true); + } + Ok(false) +} + +/// Circuit input type. +#[derive(Default)] +pub struct CircuitInput {} +impl NamedType for CircuitInput { + type Concrete = ConcreteCircuitInput; + const ID: GenericTypeId = GenericTypeId::new_inline("CircuitInput"); + + fn specialize( + &self, + context: &dyn TypeSpecializationContext, + args: &[GenericArg], + ) -> Result { + Self::Concrete::new(context, args) + } +} + +/// Defines an input for a circuit. +pub struct ConcreteCircuitInput { + /// The type info of the concrete type. + pub info: TypeInfo, + /// The index of the circuit input. + pub idx: BigInt, +} +impl ConcreteCircuitInput { + fn new( + _context: &dyn TypeSpecializationContext, + args: &[GenericArg], + ) -> Result { + let idx = args_as_single_value(args)?; + Ok(Self { + info: TypeInfo { + long_id: ConcreteTypeLongId { + generic_id: "CircuitInput".into(), + generic_args: args.to_vec(), + }, + duplicatable: false, + droppable: false, + storable: false, + zero_sized: false, + }, + idx, + }) + } +} + +impl ConcreteType for ConcreteCircuitInput { + fn info(&self) -> &TypeInfo { + &self.info + } +} + +/// Type for accumulating inputs into the circuit instance's data. +#[derive(Default)] +pub struct CircuitInputAccumulator {} +impl NamedType for CircuitInputAccumulator { + type Concrete = ConcreteCircuitInputAccumulator; + const ID: GenericTypeId = GenericTypeId::new_inline("CircuitInputAccumulator"); + + fn specialize( + &self, + context: &dyn TypeSpecializationContext, + args: &[GenericArg], + ) -> Result { + Self::Concrete::new(context, args) + } +} + +pub struct ConcreteCircuitInputAccumulator { + pub info: TypeInfo, +} + +impl ConcreteCircuitInputAccumulator { + fn new( + context: &dyn TypeSpecializationContext, + args: &[GenericArg], + ) -> Result { + let circ_ty = args_as_single_type(args)?; + validate_is_circuit(context, circ_ty)?; + Ok(Self { + info: TypeInfo { + long_id: ConcreteTypeLongId { + generic_id: "CircuitInputAccumulator".into(), + generic_args: args.to_vec(), + }, + duplicatable: false, + droppable: true, + storable: true, + zero_sized: false, + }, + }) + } +} + +impl ConcreteType for ConcreteCircuitInputAccumulator { + fn info(&self) -> &TypeInfo { + &self.info + } +} + +/// Validate that `circ_ty` is a circuit type. +fn validate_is_circuit( + context: &dyn TypeSpecializationContext, + circ_ty: ConcreteTypeId, +) -> Result<(), SpecializationError> { + let struct_generic_args = extract_type_generic_args::(context, &circ_ty)?; + + let mut gargs = struct_generic_args.iter(); + if !matches!( + gargs.next(), + Some(GenericArg::UserType(ut)) + if (*ut == UserTypeId::from_string("Tuple")) + + ) { + return Err(SpecializationError::UnsupportedGenericArg); + } + + for garg in gargs { + // Note that its enough to check the topmost types as they validate their children. + if !is_circuit_component(context, garg)? { + return Err(SpecializationError::UnsupportedGenericArg); + } + } + + Ok(()) +} + +/// Libfunc for initializing the input data for running an instance of the circuit. +#[derive(Default)] +pub struct InitCircuitData {} +impl SignatureOnlyGenericLibfunc for InitCircuitData { + const STR_ID: &'static str = "init_circuit_data"; + + fn specialize_signature( + &self, + context: &dyn SignatureSpecializationContext, + generic_args: &[GenericArg], + ) -> Result { + let range_check96_type = context.get_concrete_type(RangeCheck96Type::id(), &[])?; + let circuit_input_accumulator_ty = + context.get_concrete_type(CircuitInputAccumulator::id(), generic_args)?; + Ok(LibfuncSignature::new_non_branch_ex( + vec![ParamSignature::new(range_check96_type.clone()).with_allow_add_const()], + vec![ + OutputVarInfo { + ty: range_check96_type.clone(), + ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::AddConst { + param_idx: 0, + }), + }, + OutputVarInfo { + ty: circuit_input_accumulator_ty.clone(), + ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic), + }, + ], + SierraApChange::Known { new_vars_only: true }, + )) + } +} diff --git a/crates/cairo-lang-sierra/src/extensions/modules/const_type.rs b/crates/cairo-lang-sierra/src/extensions/modules/const_type.rs index 3cfdce8c752..6534604d36f 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/const_type.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/const_type.rs @@ -16,8 +16,8 @@ use crate::extensions::lib_func::{ use crate::extensions::type_specialization_context::TypeSpecializationContext; use crate::extensions::types::TypeInfo; use crate::extensions::{ - args_as_single_type, ConcreteType, NamedLibfunc, NamedType, OutputVarReferenceInfo, - SignatureBasedConcreteLibfunc, SpecializationError, + args_as_single_type, extract_type_generic_args, ConcreteType, NamedLibfunc, NamedType, + OutputVarReferenceInfo, SignatureBasedConcreteLibfunc, SpecializationError, }; use crate::ids::{ConcreteTypeId, GenericTypeId, UserTypeId}; use crate::program::{ConcreteTypeLongId, GenericArg}; @@ -210,19 +210,6 @@ fn extract_const_info( } } -/// Extracts the generic args of `ty`, additionally validates it is of generic type `T`. -fn extract_type_generic_args( - context: &dyn TypeSpecializationContext, - ty: &ConcreteTypeId, -) -> Result, SpecializationError> { - let long_id = context.get_type_info(ty.clone())?.long_id; - if long_id.generic_id != T::ID { - Err(SpecializationError::UnsupportedGenericArg) - } else { - Ok(long_id.generic_args) - } -} - /// Given a `args` extracts the first generic arg as type, and returns a slice pointing to the rest. fn inner_type_and_data( args: &[GenericArg], diff --git a/crates/cairo-lang-sierra/src/extensions/modules/mod.rs b/crates/cairo-lang-sierra/src/extensions/modules/mod.rs index 99a7a741e90..03b86277960 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/mod.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/mod.rs @@ -15,6 +15,7 @@ pub mod boxing; pub mod branch_align; pub mod bytes31; pub mod casts; +pub mod circuit; pub mod const_type; pub mod consts; pub mod coupon; diff --git a/crates/cairo-lang-sierra/src/extensions/modules/structure.rs b/crates/cairo-lang-sierra/src/extensions/modules/structure.rs index 8826335f6a4..794f126f0cf 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/structure.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/structure.rs @@ -59,6 +59,7 @@ impl StructConcreteType { .ok_or(SpecializationError::UnsupportedGenericArg)?; let mut duplicatable = true; let mut droppable = true; + let mut storable = true; let mut members: Vec = Vec::new(); let mut zero_sized = true; for arg in args_iter { @@ -67,7 +68,7 @@ impl StructConcreteType { .clone(); let info = context.get_type_info(ty.clone())?; if !info.storable { - return Err(SpecializationError::UnsupportedGenericArg); + storable = false; } if !info.duplicatable { duplicatable = false; @@ -86,7 +87,7 @@ impl StructConcreteType { }, duplicatable, droppable, - storable: true, + storable, zero_sized, }, members, diff --git a/crates/cairo-lang-sierra/src/extensions/test.rs b/crates/cairo-lang-sierra/src/extensions/test.rs index 5779a6ed76d..7731f542b68 100644 --- a/crates/cairo-lang-sierra/src/extensions/test.rs +++ b/crates/cairo-lang-sierra/src/extensions/test.rs @@ -188,7 +188,7 @@ impl SpecializationContext for MockSpecializationContext { #[test_case("Struct", vec![user_type_arg("name"), value_arg(5)] => Err(UnsupportedGenericArg); "Struct")] #[test_case("Struct", vec![user_type_arg("name"), type_arg("UninitializedFelt252")] - => Err(UnsupportedGenericArg); + => Ok(()); "Struct")] #[test_case("Struct", vec![type_arg("u128"), type_arg("felt252")] => Err(UnsupportedGenericArg); "Struct")] diff --git a/crates/cairo-lang-sierra/src/simulation/core.rs b/crates/cairo-lang-sierra/src/simulation/core.rs index e0134172fec..8b3db15a869 100644 --- a/crates/cairo-lang-sierra/src/simulation/core.rs +++ b/crates/cairo-lang-sierra/src/simulation/core.rs @@ -358,6 +358,7 @@ pub fn simulate< Const(_) => unimplemented!(), Coupon(_) => unimplemented!(), BoundedInt(_) => unimplemented!(), + CoreConcreteLibfunc::Circuit(_) => unimplemented!(), } } 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 0bf987540bf..74d78532a72 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 @@ -124,6 +124,7 @@ "i8_to_felt252", "i8_try_from_felt252", "i8_wide_mul", + "init_circuit_data", "into_box", "jump", "keccak_syscall", diff --git a/tests/e2e_test.rs b/tests/e2e_test.rs index 3528287b3ec..e7f65e53cff 100644 --- a/tests/e2e_test.rs +++ b/tests/e2e_test.rs @@ -58,6 +58,7 @@ cairo_lang_test_utils::test_file_test_with_runner!( box_: "box", builtin_costs: "builtin_costs", casts: "casts", + circuit: "circuit", consts: "consts", coupon: "coupon", ec: "ec", diff --git a/tests/e2e_test_data/libfuncs/circuit b/tests/e2e_test_data/libfuncs/circuit new file mode 100644 index 00000000000..a03ab674f02 --- /dev/null +++ b/tests/e2e_test_data/libfuncs/circuit @@ -0,0 +1,38 @@ +//! > init_circuit_data + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +use core::circuit::{CircuitInput, CircuitInputAccumulator, init_circuit_data}; +type MyCircuit = (CircuitInput<0>,); + +fn foo() -> CircuitInputAccumulator { + init_circuit_data::() +} + +//! > casm +[ap + 0] = [fp + -3] + 2, ap++; +[ap + 0] = [fp + -3], ap++; +[ap + 0] = [fp + -3] + 1, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 300}) + +//! > sierra_code +type RangeCheck96 = RangeCheck96 [storable: true, drop: false, dup: false, zero_sized: false]; +type core::circuit::CircuitInput::<0> = CircuitInput<0> [storable: false, drop: false, dup: false, zero_sized: false]; +type (core::circuit::CircuitInput::<0>,) = Struct> [storable: false, drop: false, dup: false, zero_sized: false]; +type CircuitInputAccumulator<(core::circuit::CircuitInput::<0>,)> = CircuitInputAccumulator<(core::circuit::CircuitInput::<0>,)> [storable: true, drop: true, dup: false, zero_sized: false]; + +libfunc init_circuit_data<(core::circuit::CircuitInput::<0>,)> = init_circuit_data<(core::circuit::CircuitInput::<0>,)>; +libfunc store_temp = store_temp; +libfunc store_temp,)>> = store_temp,)>>; + +init_circuit_data<(core::circuit::CircuitInput::<0>,)>([0]) -> ([1], [2]); // 0 +store_temp([1]) -> ([1]); // 1 +store_temp,)>>([2]) -> ([2]); // 2 +return([1], [2]); // 3 + +test::foo@0([0]: RangeCheck96) -> (RangeCheck96, CircuitInputAccumulator<(core::circuit::CircuitInput::<0>,)>);