diff --git a/core/src/main.rs b/core/src/main.rs index 6704077..276afab 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -4,8 +4,8 @@ use shenlong_core::sierra::errors::CompilerResult; use shenlong_core::sierra::llvm_compiler; fn main() -> CompilerResult<()> { llvm_compiler::Compiler::compile_from_file( - Path::new("./core/tests/test_data/sierra/division.sierra"), - Path::new("./core/tests/test_data/llvm/division.ll"), + Path::new("./core/tests/test_data/sierra/fib_array.sierra"), + Path::new("./core/tests/test_data/llvm/fib_array.ll"), Some("x86_64-pc-linux-gnu"), )?; Ok(()) diff --git a/core/src/sierra/corelib_functions/array/append.rs b/core/src/sierra/corelib_functions/array/append.rs new file mode 100644 index 0000000..af5d0ac --- /dev/null +++ b/core/src/sierra/corelib_functions/array/append.rs @@ -0,0 +1,94 @@ +use cairo_lang_sierra::ids::ConcreteTypeId; +use cairo_lang_sierra::program::{GenericArg, LibfuncDeclaration}; +use inkwell::types::BasicType; +use inkwell::AddressSpace; +use inkwell::IntPredicate::EQ; + +use crate::sierra::errors::DEBUG_NAME_EXPECTED; +use crate::sierra::llvm_compiler::Compiler; + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + /// Implementation of the LLVM IR conversion of `array_append` operation. + /// + /// # Arguments + /// + /// * `libfunc_declaration` - The corelib function declaration of `array_append`. + /// + /// # Error + /// + /// Panics if the type T has not been declared previously as all types should be declared at the + /// beginning of the sierra file. + pub fn array_append(&self, libfunc_declaration: &LibfuncDeclaration) { + let (val_ty, array_ty) = match &libfunc_declaration.long_id.generic_args[0] { + // Panics if the type has not been declared. + GenericArg::Type(ConcreteTypeId { id, debug_name }) => ( + *self.types_by_id.get(id).unwrap(), + self.types_by_name + .get(&format!("Array<{:#}>", debug_name.clone().expect(DEBUG_NAME_EXPECTED).to_string())) + .unwrap() + .into_struct_type(), + ), + // Not sure if dup can dup user defined types + GenericArg::UserType(_) => todo!(), + _ => panic!("array_push only takes type or user type"), + }; + + // fn array_append>(array: Array, val: T) -> Array + let func = self.module.add_function( + libfunc_declaration.id.debug_name.clone().expect(DEBUG_NAME_EXPECTED).to_string().as_str(), + array_ty.fn_type(&[array_ty.into(), val_ty.into()], false), + None, + ); + let arg = func.get_first_param().unwrap(); + self.builder.position_at_end(self.context.append_basic_block(func, "entry")); + // Get the felt type because length and capacity are felts. + let felt_type = *self.types_by_name.get("felt").unwrap(); + // Get the ptr of the array. + let array_ptr = self.builder.build_alloca(array_ty, "array_ptr"); + self.builder.build_store(array_ptr, arg); + // Get the ptr to the values. + let ptr_ptr = self.builder.build_struct_gep(array_ty, array_ptr, 0, "ptr_ptr").unwrap(); + let ptr = self.builder.build_load( + array_ty.get_field_type_at_index(0).unwrap().ptr_type(AddressSpace::default()), + ptr_ptr, + "ptr", + ); + // Get the array current length. + let len_ptr = self.builder.build_struct_gep(array_ty, array_ptr, 1, "len_ptr").unwrap(); + let len = self.builder.build_load(felt_type, len_ptr, "len").into_int_value(); + // Get the array current capacity. + let capacity_ptr = self.builder.build_struct_gep(array_ty, array_ptr, 2, "capacity_ptr").unwrap(); + let capacity = self.builder.build_load(felt_type, capacity_ptr, "capacity").into_int_value(); + + // Check if the array has enough capacity to add a new value. + let check_array_cap = self.builder.build_int_compare(EQ, len, capacity, "is_array_big_enough"); + // if then + let then_bb = self.context.append_basic_block(func, "then"); + // finally + let finally_bb = self.context.append_basic_block(func, "finally"); + + self.builder.build_conditional_branch(check_array_cap, then_bb, finally_bb); + self.builder.position_at_end(then_bb); + let new_cap = capacity.const_mul(capacity.get_type().const_int(2, false)); + let dest = self.builder.build_array_malloc(val_ty, new_cap, "new_arr").unwrap(); + self.builder.build_memcpy(dest, 2, ptr.into_pointer_value(), 2, capacity).unwrap(); + self.builder.build_store(capacity_ptr, new_cap); + self.builder.build_unconditional_branch(finally_bb); + + self.builder.position_at_end(finally_bb); + + let empty_cell_id = len.const_add(len.get_type().const_int(1, false)); + self.builder.build_store(len_ptr, empty_cell_id); + let empty_cell; + unsafe { + empty_cell = self.builder.build_gep( + array_ty.get_field_type_at_index(0).unwrap(), + ptr.into_pointer_value(), + &[empty_cell_id], + "empty_cell", + ); + } + self.builder.build_store(empty_cell, func.get_last_param().unwrap()); + self.builder.build_return(Some(&self.builder.build_load(array_ty, array_ptr, "res"))); + } +} diff --git a/core/src/sierra/corelib_functions/array/mod.rs b/core/src/sierra/corelib_functions/array/mod.rs new file mode 100644 index 0000000..8723d24 --- /dev/null +++ b/core/src/sierra/corelib_functions/array/mod.rs @@ -0,0 +1,2 @@ +pub mod append; +pub mod new; diff --git a/core/src/sierra/corelib_functions/array/new.rs b/core/src/sierra/corelib_functions/array/new.rs new file mode 100644 index 0000000..10382fd --- /dev/null +++ b/core/src/sierra/corelib_functions/array/new.rs @@ -0,0 +1,40 @@ +use cairo_lang_sierra::ids::ConcreteTypeId; +use cairo_lang_sierra::program::{GenericArg, LibfuncDeclaration}; + +use crate::sierra::errors::DEBUG_NAME_EXPECTED; +use crate::sierra::llvm_compiler::Compiler; + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + /// Implementation of the LLVM IR conversion of `array_new` operation. + /// + /// # Arguments + /// + /// * `libfunc_declaration` - The corelib function declaration of `array_new`. + /// + /// # Error + /// + /// Panics if the type T has not been declared previously as all types should be declared at the + /// beginning of the sierra file. + pub fn array_new(&self, libfunc_declaration: &LibfuncDeclaration) { + let val_name = match &libfunc_declaration.long_id.generic_args[0] { + // Panics if the type has not been declared. + GenericArg::Type(ConcreteTypeId { id: _, debug_name }) => debug_name.clone().expect(DEBUG_NAME_EXPECTED), + // Not sure if dup can array_new user defined types + GenericArg::UserType(_) => todo!(), + _ => panic!("Dup only takes type or user type"), + }; + let ret_type = self.types_by_name.get(&(format!("Array<{:}", val_name) + ">")).unwrap().into_struct_type(); + + let arr_size = ret_type.get_field_type_at_index(1).unwrap().into_int_type().const_int(2, false); + let arr_ptr = + self.builder.build_array_malloc(ret_type.get_field_type_at_index(0).unwrap(), arr_size, "ret").unwrap(); + let arr = ret_type.const_named_struct(&[arr_ptr.into(), arr_size.into()]); + let func = self.module.add_function( + libfunc_declaration.id.debug_name.clone().expect(DEBUG_NAME_EXPECTED).to_string().as_str(), + ret_type.fn_type(&[], false), + None, + ); + self.builder.position_at_end(self.context.append_basic_block(func, "entry")); + self.builder.build_return(Some(&arr)); + } +} diff --git a/core/src/sierra/corelib_functions/math/constants.rs b/core/src/sierra/corelib_functions/math/constants.rs index 06a0b36..8858f40 100644 --- a/core/src/sierra/corelib_functions/math/constants.rs +++ b/core/src/sierra/corelib_functions/math/constants.rs @@ -6,22 +6,21 @@ use crate::sierra::errors::DEBUG_NAME_EXPECTED; use crate::sierra::llvm_compiler::Compiler; impl<'a, 'ctx> Compiler<'a, 'ctx> { - /// Implementation of the LLVM IR conversion of a felt constant. + /// Implementation of the LLVM IR conversion of an int constant. /// /// # Arguments /// - /// * `libfunc_declaration` - The corelib function declaration of felt_const. + /// * `libfunc_declaration` - The corelib function declaration of {un|felt}_const. /// /// # Error /// - /// Panics if the felt type has not been declared previously. - pub fn felt_const(&mut self, libfunc_declaration: &LibfuncDeclaration) { - // We could hardcode the LLVM IR type for felt but this adds a check. + /// Panics if the int type has not been declared previously. + pub fn int_const(&mut self, libfunc_declaration: &LibfuncDeclaration, int_ty: &str) { let func_name = libfunc_declaration.id.debug_name.as_ref().expect(DEBUG_NAME_EXPECTED).as_str(); - let return_type = self.types_by_name.get("felt").expect("Can't get felt from name"); - let debug_return_type = *self.debug.types_by_name.get("felt").expect("Can't get felt from name"); + let return_type = self.types_by_name.get(int_ty).expect("Can't get int type from name"); + let debug_return_type = *self.debug.types_by_name.get(int_ty).expect("Can't get int type from name"); - // fn felt_const() -> felt + // fn int_const() -> int let func = self.module.add_function(func_name, return_type.fn_type(&[], false), None); self.builder.position_at_end(self.context.append_basic_block(func, "entry")); @@ -35,10 +34,10 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { .as_basic_type_enum() .into_int_type() .const_int_from_string(val.to_string().as_str(), StringRadix::Decimal) - .expect("Couldn't convert to string the felt constant value") + .expect("Couldn't convert to string the int constant value") } else { // If the constant doesn't have any value it panics because a constant should have a value. - panic!("No value for felt constant") + panic!("No value for constant") }; let inst = self.builder.build_return(Some(&ret)); diff --git a/core/src/sierra/corelib_functions/memory/store_temp.rs b/core/src/sierra/corelib_functions/memory/store_temp.rs index b101398..878deba 100644 --- a/core/src/sierra/corelib_functions/memory/store_temp.rs +++ b/core/src/sierra/corelib_functions/memory/store_temp.rs @@ -22,7 +22,7 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { // Get the type that this store_temp function has to handle let type_id = match &libfunc_declaration.long_id.generic_args[0] { - GenericArg::Type(ConcreteTypeId { id, debug_name: _ }) => *id, + GenericArg::Type(ConcreteTypeId { id, debug_name: _debug_name }) => *id, // Not sure if store_temp can store_temp user defined types GenericArg::UserType(_) => todo!(), _val => { diff --git a/core/src/sierra/corelib_functions/mod.rs b/core/src/sierra/corelib_functions/mod.rs index 0ab2e51..a8a6128 100644 --- a/core/src/sierra/corelib_functions/mod.rs +++ b/core/src/sierra/corelib_functions/mod.rs @@ -1,3 +1,4 @@ +pub mod array; pub mod boxes; pub mod functions; pub mod math; diff --git a/core/src/sierra/process/corelib.rs b/core/src/sierra/process/corelib.rs index 9ca3fa5..8fcc12a 100644 --- a/core/src/sierra/process/corelib.rs +++ b/core/src/sierra/process/corelib.rs @@ -5,7 +5,7 @@ use tracing::debug; use crate::sierra::errors::CompilerResult; use crate::sierra::llvm_compiler::{CompilationState, Compiler}; -use crate::sierra::types::felt::DOUBLE_FELT_INT_WIDTH; +use crate::sierra::types::math::felt::DOUBLE_FELT_INT_WIDTH; pub const PRINT_FELT_FUNC: &str = "print_felt"; pub const PRINT_DOUBLE_FELT_FUNC: &str = "print_double_felt"; @@ -50,6 +50,8 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { debug!(libfunc_name, "processing"); // Each core lib function is known match libfunc_name { + "array_new" => self.array_new(libfunc_declaration), + "array_append" => self.array_append(libfunc_declaration), // Align branches after a match/if else or anything that creates branches. "branch_align" => debug!(libfunc_name, "ignored for now"), // Drops a variables (in sierra everything has to be used exactly once so if a variable is created and @@ -61,7 +63,7 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { // Addition for felt type. `felt + felt` "felt_add" => self.felt_add(libfunc_declaration), // Define a constant of type felt. `const one = 1;` - "felt_const" => self.felt_const(libfunc_declaration), + "felt_const" => self.int_const(libfunc_declaration, "felt"), // Check if a felt is zero. `felt == 0` "felt_is_zero" => debug!(libfunc_name, "treated in the statements"), // Multiplication for felt type. `felt * felt` @@ -86,6 +88,7 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { "struct_construct" => self.struct_construct(libfunc_declaration), // Deconstruct a struct. (Get each struct field in its own variable). "struct_deconstruct" => self.struct_deconstruct(libfunc_declaration), + "u32_const" => self.int_const(libfunc_declaration, "u32"), // Converts a `Box` to `T`. "unbox" => self.unbox(libfunc_declaration), _ => debug!(libfunc_name, "not implemented"), diff --git a/core/src/sierra/process/debug.rs b/core/src/sierra/process/debug.rs index 5b0d157..b9d2f78 100644 --- a/core/src/sierra/process/debug.rs +++ b/core/src/sierra/process/debug.rs @@ -2,7 +2,7 @@ use tracing::debug; use crate::sierra::errors::CompilerResult; use crate::sierra::llvm_compiler::{CompilationState, Compiler}; -use crate::sierra::types::felt::DOUBLE_FELT_INT_WIDTH; +use crate::sierra::types::math::felt::DOUBLE_FELT_INT_WIDTH; /// Implementation for the type processing for the compiler. impl<'a, 'ctx> Compiler<'a, 'ctx> { diff --git a/core/src/sierra/process/types.rs b/core/src/sierra/process/types.rs index 7f3e0a2..d0e0684 100644 --- a/core/src/sierra/process/types.rs +++ b/core/src/sierra/process/types.rs @@ -30,6 +30,16 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { "Box" => self.sierra_box(type_declaration), // Regular struct "Struct" => self.sierra_struct(type_declaration), + // Array + "Array" => self.array(type_declaration), + // Regular u32 + "u32" => self.u32(type_declaration), + // RangeCheck ptr + "RangeCheck" => self.range_check(type_declaration), + // Custom Enum + "Enum" => self.sierra_enum(type_declaration), + // Frozen ref + "Snapshot" => self.snapshot(type_declaration), _ => debug!(type_name, "unimplemented type"), } } diff --git a/core/src/sierra/types/array.rs b/core/src/sierra/types/array.rs new file mode 100644 index 0000000..fc5d516 --- /dev/null +++ b/core/src/sierra/types/array.rs @@ -0,0 +1,45 @@ +use cairo_lang_sierra::ids::ConcreteTypeId; +use cairo_lang_sierra::program::{GenericArg, TypeDeclaration}; +use inkwell::types::BasicType; +use inkwell::AddressSpace; + +use crate::sierra::errors::DEBUG_NAME_EXPECTED; +use crate::sierra::llvm_compiler::Compiler; + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + pub fn array(&mut self, type_declaration: &TypeDeclaration) { + match &type_declaration.long_id.generic_args[0] { + GenericArg::Type(ConcreteTypeId { id, debug_name }) => { + let val_type = self.types_by_id.get(id).unwrap().into_int_type(); + // Get the arrya value type name. + let debug_name = debug_name.clone().expect(DEBUG_NAME_EXPECTED).to_string(); + let array_debug_name = format!("Array<{:#}>", debug_name); + // Array capacity and len are felts for now. The reason for that is that there is no specific + // reason. Array { ptr: T*, len: felt, capacity: felt } + let felt_type = *self.types_by_name.get("felt").unwrap(); + let ty = self + .context + .struct_type(&[val_type.ptr_type(AddressSpace::default()).into(), felt_type, felt_type], false); + // Create debug type for the array pointer. + let ptr_debug = self.debug.create_type(9090909009, "arr_ptr", 64); + // Save Array in the types map. + self.types_by_name.insert(array_debug_name.clone(), ty.as_basic_type_enum()); + // Save the type by its id. + self.types_by_id.insert(type_declaration.id.id, ty.as_basic_type_enum()); + // Get the felt debug type. + let felt_debug = *self.debug.types_by_name.get("felt").unwrap(); + // Create debug struct type for array. + self.debug.create_struct( + type_declaration.id.id, + &array_debug_name, + &ty, + &[ptr_debug, felt_debug, felt_debug], + ) + } + GenericArg::UserType(_) => todo!(), + _ => { + panic!("Arrays only takes type or user type") + } + }; + } +} diff --git a/core/src/sierra/types/builtins/mod.rs b/core/src/sierra/types/builtins/mod.rs new file mode 100644 index 0000000..37fcad5 --- /dev/null +++ b/core/src/sierra/types/builtins/mod.rs @@ -0,0 +1 @@ +pub mod range_check; diff --git a/core/src/sierra/types/builtins/range_check.rs b/core/src/sierra/types/builtins/range_check.rs new file mode 100644 index 0000000..41b637c --- /dev/null +++ b/core/src/sierra/types/builtins/range_check.rs @@ -0,0 +1,13 @@ +use cairo_lang_sierra::program::TypeDeclaration; +use inkwell::types::BasicType; + +use crate::sierra::llvm_compiler::Compiler; + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + pub fn range_check(&mut self, type_declaration: &TypeDeclaration) { + let ty = self.context.custom_width_int_type(0).as_basic_type_enum(); + self.debug.create_type(type_declaration.id.id, "RangeCheck", 0); + self.types_by_name.insert("RangeCheck".to_owned(), ty); + self.types_by_id.insert(type_declaration.id.id, ty); + } +} diff --git a/core/src/sierra/types/felt.rs b/core/src/sierra/types/math/felt.rs similarity index 100% rename from core/src/sierra/types/felt.rs rename to core/src/sierra/types/math/felt.rs diff --git a/core/src/sierra/types/math/mod.rs b/core/src/sierra/types/math/mod.rs new file mode 100644 index 0000000..7ad0835 --- /dev/null +++ b/core/src/sierra/types/math/mod.rs @@ -0,0 +1,3 @@ +pub mod felt; +pub mod non_zero; +pub mod sierra_u32; diff --git a/core/src/sierra/types/non_zero.rs b/core/src/sierra/types/math/non_zero.rs similarity index 100% rename from core/src/sierra/types/non_zero.rs rename to core/src/sierra/types/math/non_zero.rs diff --git a/core/src/sierra/types/math/sierra_u32.rs b/core/src/sierra/types/math/sierra_u32.rs new file mode 100644 index 0000000..ec964f7 --- /dev/null +++ b/core/src/sierra/types/math/sierra_u32.rs @@ -0,0 +1,13 @@ +use cairo_lang_sierra::program::TypeDeclaration; +use inkwell::types::BasicType; + +use crate::sierra::llvm_compiler::Compiler; + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + pub fn u32(&mut self, type_declaration: &TypeDeclaration) { + let ty = self.context.custom_width_int_type(32).as_basic_type_enum(); + self.types_by_name.insert("u32".to_owned(), ty); + self.types_by_id.insert(type_declaration.id.id, ty); + self.debug.create_type(type_declaration.id.id, "u32", 32); + } +} diff --git a/core/src/sierra/types/mod.rs b/core/src/sierra/types/mod.rs index c21a35c..050dc06 100644 --- a/core/src/sierra/types/mod.rs +++ b/core/src/sierra/types/mod.rs @@ -1,4 +1,7 @@ -pub mod felt; -pub mod non_zero; +pub mod array; +pub mod builtins; +pub mod math; pub mod sierra_box; +pub mod sierra_enum; pub mod sierra_struct; +pub mod snapshot; diff --git a/core/src/sierra/types/sierra_enum.rs b/core/src/sierra/types/sierra_enum.rs new file mode 100644 index 0000000..4c4bf62 --- /dev/null +++ b/core/src/sierra/types/sierra_enum.rs @@ -0,0 +1,39 @@ +use cairo_lang_sierra::ids::{ConcreteTypeId, UserTypeId}; +use cairo_lang_sierra::program::{GenericArg, TypeDeclaration}; +use inkwell::types::BasicType; + +use crate::sierra::errors::DEBUG_NAME_EXPECTED; +use crate::sierra::llvm_compiler::Compiler; + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + pub fn sierra_enum(&mut self, type_declaration: &TypeDeclaration) { + let mut args = vec![]; + let mut debug_args = vec![]; + let mut ty_name = "".to_owned(); + for generic_arg in type_declaration.long_id.generic_args.iter() { + match generic_arg { + GenericArg::Type(ConcreteTypeId { id, debug_name: _ }) => { + args.push( + self.types_by_id + .get(id) + .expect("Type should have been defined before struct") + .as_basic_type_enum(), + ); + debug_args + .push(*self.debug.types_by_id.get(id).expect("Type should have been defined before struct")); + } + + GenericArg::UserType(UserTypeId { id: _id, debug_name }) => { + ty_name = debug_name.clone().expect(DEBUG_NAME_EXPECTED).to_string(); + } + _val => { + panic!("store_temp only takes type or user type") + } + }; + } + let ty = self.context.struct_type(&args, false); + self.types_by_id.insert(type_declaration.id.id, ty.as_basic_type_enum()); + self.types_by_name.insert(ty_name.clone(), ty.as_basic_type_enum()); + self.debug.create_struct(type_declaration.id.id, &ty_name, &ty, &debug_args); + } +} diff --git a/core/src/sierra/types/sierra_struct.rs b/core/src/sierra/types/sierra_struct.rs index 24ba80f..9e7090e 100644 --- a/core/src/sierra/types/sierra_struct.rs +++ b/core/src/sierra/types/sierra_struct.rs @@ -16,7 +16,7 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { for generic_arg in type_declaration.long_id.generic_args.iter() { match generic_arg { - GenericArg::Type(ConcreteTypeId { id, debug_name: _ }) => { + GenericArg::Type(ConcreteTypeId { id, debug_name: _debug_name }) => { args.push( self.types_by_id .get(id) diff --git a/core/src/sierra/types/snapshot.rs b/core/src/sierra/types/snapshot.rs new file mode 100644 index 0000000..8685196 --- /dev/null +++ b/core/src/sierra/types/snapshot.rs @@ -0,0 +1,22 @@ +use cairo_lang_sierra::ids::ConcreteTypeId; +use cairo_lang_sierra::program::{GenericArg, TypeDeclaration}; + +use crate::sierra::llvm_compiler::Compiler; + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + pub fn snapshot(&mut self, type_declaration: &TypeDeclaration) { + let ty = + if let GenericArg::Type(ConcreteTypeId { id, debug_name: _ }) = type_declaration.long_id.generic_args[0] { + *self.types_by_id.get(&id).expect("Type should have been defined before struct") + } else { + panic!("store_temp only takes type or user type") + }; + + let underlying_ty_name = type_declaration.long_id.generic_args[0].to_string(); + self.types_by_id.insert(type_declaration.id.id, ty); + self.types_by_name.insert(underlying_ty_name.clone(), ty); + let debug_ty = self.debug.types_by_name.get(&underlying_ty_name).unwrap(); + self.debug.types_by_id.insert(type_declaration.id.id, *debug_ty); + self.debug.types_by_name.insert(format!("Snapshot<{:#}>", underlying_ty_name), *debug_ty); + } +} diff --git a/core/tests/test_data/cairo/fib_array.cairo b/core/tests/test_data/cairo/fib_array.cairo new file mode 100644 index 0000000..e70dac0 --- /dev/null +++ b/core/tests/test_data/cairo/fib_array.cairo @@ -0,0 +1,22 @@ +use array::ArrayTrait; + +// Returns an array of size n with the values of the Fibonacci sequence, the length of the array, +// and the value of the last element. +fn fib(n: usize) -> (Array::, felt, usize) { + let mut arr = ArrayTrait::new(); + arr.append(1); + arr.append(1); + let mut arr = fib_inner(:n, :arr); + let len = arr.len(); + let last = arr.at(len - 1_usize); + return (arr, *last, len); +} + +fn fib_inner(n: usize, mut arr: Array::) -> Array:: { + let length = arr.len(); + if n <= length { + return arr; + } + arr.append(*arr.at(length - 1_usize) + *arr.at(length - 2_usize)); + fib_inner(:n, :arr) +} diff --git a/core/tests/test_data/sierra/fib_array.sierra b/core/tests/test_data/sierra/fib_array.sierra new file mode 100644 index 0000000..5d2fd62 --- /dev/null +++ b/core/tests/test_data/sierra/fib_array.sierra @@ -0,0 +1,360 @@ +type felt = felt; +type Array = Array; +type Unit = Struct; +type RangeCheck = RangeCheck; +type u32 = u32; +type core::PanicResult::> = Enum>, Array, Array>; +type Tuple, felt, u32> = Struct, felt, u32>; +type core::PanicResult::<(core::array::Array::, core::felt, core::integer::u32)> = Enum, core::felt, core::integer::u32)>, Tuple, felt, u32>, Array>; +type Snapshot> = Snapshot>; +type core::PanicResult:: = Enum, u32, Array>; +type core::PanicResult::<@core::felt> = Enum, felt, Array>; +type Uninitialized = Uninitialized; +type core::bool = Enum; +type core::result::Result:: = Enum, u32, u32>; + +libfunc revoke_ap_tracking = revoke_ap_tracking; +libfunc array_new = array_new; +libfunc felt_const<1> = felt_const<1>; +libfunc store_temp = store_temp; +libfunc array_append = array_append; +libfunc struct_construct = struct_construct; +libfunc drop = drop; +libfunc store_temp = store_temp; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc function_call = function_call; +libfunc enum_match>> = enum_match>>; +libfunc branch_align = branch_align; +libfunc jump = jump; +libfunc enum_init, core::felt, core::integer::u32)>, 1> = enum_init, core::felt, core::integer::u32)>, 1>; +libfunc store_temp, core::felt, core::integer::u32)>> = store_temp, core::felt, core::integer::u32)>>; +libfunc snapshot_take> = snapshot_take>; +libfunc array_len = array_len; +libfunc u32_const<1> = u32_const<1>; +libfunc dup = dup; +libfunc function_call = function_call; +libfunc enum_match> = enum_match>; +libfunc drop> = drop>; +libfunc drop = drop; +libfunc drop>> = drop>>; +libfunc store_temp>> = store_temp>>; +libfunc function_call::at> = function_call::at>; +libfunc enum_match> = enum_match>; +libfunc rename = rename; +libfunc struct_construct, felt, u32>> = struct_construct, felt, u32>>; +libfunc enum_init, core::felt, core::integer::u32)>, 0> = enum_init, core::felt, core::integer::u32)>, 0>; +libfunc alloc_local = alloc_local; +libfunc finalize_locals = finalize_locals; +libfunc store_local = store_local; +libfunc u32_le = u32_le; +libfunc enum_init = enum_init; +libfunc store_temp = store_temp; +libfunc enum_init = enum_init; +libfunc enum_match = enum_match; +libfunc enum_init>, 0> = enum_init>, 0>; +libfunc store_temp>> = store_temp>>; +libfunc enum_init>, 1> = enum_init>, 1>; +libfunc u32_const<2> = u32_const<2>; +libfunc drop = drop; +libfunc felt_add = felt_add; +libfunc u32_overflowing_sub = u32_overflowing_sub; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp> = store_temp>; +libfunc enum_init, 1> = enum_init, 1>; +libfunc felt_const<155785504329508738615720351733824384887> = felt_const<155785504329508738615720351733824384887>; +libfunc rename> = rename>; +libfunc function_call::expect> = function_call::expect>; +libfunc enum_init, 1> = enum_init, 1>; +libfunc store_temp> = store_temp>; +libfunc enum_init, 0> = enum_init, 0>; +libfunc function_call> = function_call>; +libfunc enum_init, 1> = enum_init, 1>; +libfunc store_temp> = store_temp>; +libfunc enum_init, 0> = enum_init, 0>; +libfunc enum_match> = enum_match>; +libfunc array_get = array_get; +libfunc felt_const<1459518160254581538105416103334897598843479155> = felt_const<1459518160254581538105416103334897598843479155>; + +revoke_ap_tracking() -> (); +array_new() -> ([2]); +felt_const<1>() -> ([3]); +store_temp([3]) -> ([3]); +array_append([2], [3]) -> ([4]); +struct_construct() -> ([5]); +drop([5]) -> (); +felt_const<1>() -> ([6]); +store_temp([6]) -> ([6]); +array_append([4], [6]) -> ([7]); +struct_construct() -> ([8]); +drop([8]) -> (); +store_temp([0]) -> ([11]); +store_temp([1]) -> ([12]); +store_temp>([7]) -> ([13]); +function_call([11], [12], [13]) -> ([9], [10]); +enum_match>>([10]) { fallthrough([14]) 20([15]) }; +branch_align() -> (); +store_temp>([14]) -> ([16]); +jump() { 25() }; +branch_align() -> (); +enum_init, core::felt, core::integer::u32)>, 1>([15]) -> ([17]); +store_temp([9]) -> ([18]); +store_temp, core::felt, core::integer::u32)>>([17]) -> ([19]); +return([18], [19]); +snapshot_take>([16]) -> ([20], [21]); +array_len([21]) -> ([22]); +snapshot_take>([20]) -> ([23], [24]); +u32_const<1>() -> ([25]); +store_temp([9]) -> ([28]); +store_temp([22]) -> ([29]); +dup([29]) -> ([29], [22]); +store_temp([25]) -> ([30]); +function_call([28], [29], [30]) -> ([26], [27]); +enum_match>([27]) { fallthrough([31]) 38([32]) }; +branch_align() -> (); +store_temp([31]) -> ([33]); +jump() { 46() }; +branch_align() -> (); +drop>([23]) -> (); +drop([22]) -> (); +drop>>([24]) -> (); +enum_init, core::felt, core::integer::u32)>, 1>([32]) -> ([34]); +store_temp([26]) -> ([35]); +store_temp, core::felt, core::integer::u32)>>([34]) -> ([36]); +return([35], [36]); +store_temp([26]) -> ([39]); +store_temp>>([24]) -> ([40]); +store_temp([33]) -> ([41]); +function_call::at>([39], [40], [41]) -> ([37], [38]); +enum_match>([38]) { fallthrough([42]) 54([43]) }; +branch_align() -> (); +store_temp([42]) -> ([44]); +jump() { 61() }; +branch_align() -> (); +drop([22]) -> (); +drop>([23]) -> (); +enum_init, core::felt, core::integer::u32)>, 1>([43]) -> ([45]); +store_temp([37]) -> ([46]); +store_temp, core::felt, core::integer::u32)>>([45]) -> ([47]); +return([46], [47]); +rename([44]) -> ([48]); +struct_construct, felt, u32>>([23], [48], [22]) -> ([49]); +enum_init, core::felt, core::integer::u32)>, 0>([49]) -> ([50]); +store_temp([37]) -> ([51]); +store_temp, core::felt, core::integer::u32)>>([50]) -> ([52]); +return([51], [52]); +alloc_local() -> ([4]); +finalize_locals() -> (); +revoke_ap_tracking() -> (); +snapshot_take>([2]) -> ([5], [6]); +array_len([6]) -> ([3]); +dup([1]) -> ([1], [9]); +store_local([4], [3]) -> ([3]); +dup([3]) -> ([3], [10]); +u32_le([0], [9], [10]) { fallthrough([7]) 82([8]) }; +branch_align() -> (); +struct_construct() -> ([11]); +enum_init([11]) -> ([12]); +store_temp([7]) -> ([13]); +store_temp([12]) -> ([14]); +jump() { 87() }; +branch_align() -> (); +struct_construct() -> ([15]); +enum_init([15]) -> ([16]); +store_temp([8]) -> ([13]); +store_temp([16]) -> ([14]); +enum_match([14]) { fallthrough([17]) 91([18]) }; +branch_align() -> (); +drop([17]) -> (); +jump() { 99() }; +branch_align() -> (); +drop([18]) -> (); +drop([1]) -> (); +drop([3]) -> (); +enum_init>, 0>([5]) -> ([19]); +store_temp([13]) -> ([20]); +store_temp>>([19]) -> ([21]); +return([20], [21]); +snapshot_take>([5]) -> ([22], [23]); +u32_const<1>() -> ([24]); +store_temp([13]) -> ([27]); +dup([3]) -> ([3], [28]); +store_temp([28]) -> ([28]); +store_temp([24]) -> ([29]); +function_call([27], [28], [29]) -> ([25], [26]); +enum_match>([26]) { fallthrough([30]) 110([31]) }; +branch_align() -> (); +store_temp([30]) -> ([32]); +jump() { 119() }; +branch_align() -> (); +drop>([22]) -> (); +drop([1]) -> (); +drop([3]) -> (); +drop>>([23]) -> (); +enum_init>, 1>([31]) -> ([33]); +store_temp([25]) -> ([34]); +store_temp>>([33]) -> ([35]); +return([34], [35]); +store_temp([25]) -> ([38]); +store_temp>>([23]) -> ([39]); +store_temp([32]) -> ([40]); +function_call::at>([38], [39], [40]) -> ([36], [37]); +enum_match>([37]) { fallthrough([41]) 127([42]) }; +branch_align() -> (); +store_temp([41]) -> ([43]); +jump() { 135() }; +branch_align() -> (); +drop>([22]) -> (); +drop([1]) -> (); +drop([3]) -> (); +enum_init>, 1>([42]) -> ([44]); +store_temp([36]) -> ([45]); +store_temp>>([44]) -> ([46]); +return([45], [46]); +rename([43]) -> ([47]); +snapshot_take>([22]) -> ([48], [49]); +u32_const<2>() -> ([50]); +store_temp([36]) -> ([53]); +store_temp([3]) -> ([54]); +store_temp([50]) -> ([55]); +function_call([53], [54], [55]) -> ([51], [52]); +enum_match>([52]) { fallthrough([56]) 146([57]) }; +branch_align() -> (); +store_temp([56]) -> ([58]); +jump() { 155() }; +branch_align() -> (); +drop([47]) -> (); +drop([1]) -> (); +drop>([48]) -> (); +drop>>([49]) -> (); +enum_init>, 1>([57]) -> ([59]); +store_temp([51]) -> ([60]); +store_temp>>([59]) -> ([61]); +return([60], [61]); +store_temp([51]) -> ([64]); +store_temp>>([49]) -> ([65]); +store_temp([58]) -> ([66]); +function_call::at>([64], [65], [66]) -> ([62], [63]); +enum_match>([63]) { fallthrough([67]) 163([68]) }; +branch_align() -> (); +store_temp([67]) -> ([69]); +jump() { 171() }; +branch_align() -> (); +drop([1]) -> (); +drop>([48]) -> (); +drop([47]) -> (); +enum_init>, 1>([68]) -> ([70]); +store_temp([62]) -> ([71]); +store_temp>>([70]) -> ([72]); +return([71], [72]); +rename([69]) -> ([73]); +felt_add([47], [73]) -> ([74]); +store_temp([74]) -> ([74]); +array_append([48], [74]) -> ([75]); +struct_construct() -> ([76]); +drop([76]) -> (); +store_temp([62]) -> ([79]); +store_temp([1]) -> ([80]); +store_temp>([75]) -> ([81]); +function_call([79], [80], [81]) -> ([77], [78]); +enum_match>>([78]) { fallthrough([82]) 185([83]) }; +branch_align() -> (); +store_temp>([82]) -> ([84]); +jump() { 190() }; +branch_align() -> (); +enum_init>, 1>([83]) -> ([85]); +store_temp([77]) -> ([86]); +store_temp>>([85]) -> ([87]); +return([86], [87]); +enum_init>, 0>([84]) -> ([88]); +store_temp([77]) -> ([89]); +store_temp>>([88]) -> ([90]); +return([89], [90]); +u32_overflowing_sub([0], [1], [2]) { fallthrough([3], [4]) 200([5], [6]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([7]); +store_temp([3]) -> ([8]); +store_temp>([7]) -> ([9]); +jump() { 204() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([10]); +store_temp([5]) -> ([8]); +store_temp>([10]) -> ([9]); +felt_const<155785504329508738615720351733824384887>() -> ([11]); +rename>([9]) -> ([13]); +store_temp([11]) -> ([14]); +function_call::expect>([13], [14]) -> ([12]); +enum_match>([12]) { fallthrough([15]) 212([16]) }; +branch_align() -> (); +store_temp([15]) -> ([17]); +jump() { 217() }; +branch_align() -> (); +enum_init, 1>([16]) -> ([18]); +store_temp([8]) -> ([19]); +store_temp>([18]) -> ([20]); +return([19], [20]); +enum_init, 0>([17]) -> ([21]); +store_temp([8]) -> ([22]); +store_temp>([21]) -> ([23]); +return([22], [23]); +store_temp([0]) -> ([5]); +store_temp>>([1]) -> ([6]); +store_temp([2]) -> ([7]); +function_call>([5], [6], [7]) -> ([3], [4]); +enum_match>([4]) { fallthrough([8]) 229([9]) }; +branch_align() -> (); +store_temp([8]) -> ([10]); +jump() { 234() }; +branch_align() -> (); +enum_init, 1>([9]) -> ([11]); +store_temp([3]) -> ([12]); +store_temp>([11]) -> ([13]); +return([12], [13]); +enum_init, 0>([10]) -> ([14]); +store_temp([3]) -> ([15]); +store_temp>([14]) -> ([16]); +return([15], [16]); +enum_match>([0]) { fallthrough([2]) 243([3]) }; +branch_align() -> (); +drop([1]) -> (); +store_temp([2]) -> ([4]); +jump() { 252() }; +branch_align() -> (); +drop([3]) -> (); +array_new() -> ([5]); +array_append([5], [1]) -> ([6]); +struct_construct() -> ([7]); +drop([7]) -> (); +enum_init, 1>([6]) -> ([8]); +store_temp>([8]) -> ([9]); +return([9]); +enum_init, 0>([4]) -> ([10]); +store_temp>([10]) -> ([11]); +return([11]); +array_get([0], [1], [2]) { fallthrough([3], [4]) 260([5]) }; +branch_align() -> (); +store_temp([3]) -> ([6]); +store_temp([4]) -> ([7]); +jump() { 271() }; +branch_align() -> (); +struct_construct() -> ([8]); +drop([8]) -> (); +array_new() -> ([9]); +felt_const<1459518160254581538105416103334897598843479155>() -> ([10]); +store_temp([10]) -> ([10]); +array_append([9], [10]) -> ([11]); +enum_init, 1>([11]) -> ([12]); +store_temp([5]) -> ([13]); +store_temp>([12]) -> ([14]); +return([13], [14]); +enum_init, 0>([7]) -> ([15]); +store_temp([6]) -> ([16]); +store_temp>([15]) -> ([17]); +return([16], [17]); + +fib_array::fib_array::fib@0([0]: RangeCheck, [1]: u32) -> (RangeCheck, core::PanicResult::<(core::array::Array::, core::felt, core::integer::u32)>); +fib_array::fib_array::fib_inner@67([0]: RangeCheck, [1]: u32, [2]: Array) -> (RangeCheck, core::PanicResult::>); +core::integer::U32Sub::sub@194([0]: RangeCheck, [1]: u32, [2]: u32) -> (RangeCheck, core::PanicResult::); +core::array::ArrayImpl::::at@221([0]: RangeCheck, [1]: Snapshot>, [2]: u32) -> (RangeCheck, core::PanicResult::<@core::felt>); +core::result::ResultTraitImpl::::expect@238([0]: core::result::Result::, [1]: felt) -> (core::PanicResult::); +core::array::array_at::@255([0]: RangeCheck, [1]: Snapshot>, [2]: u32) -> (RangeCheck, core::PanicResult::<@core::felt>);