Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

Fib using array + panic + some u32 #63

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions core/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down
94 changes: 94 additions & 0 deletions core/src/sierra/corelib_functions/array/append.rs
Original file line number Diff line number Diff line change
@@ -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<T>` operation.
///
/// # Arguments
///
/// * `libfunc_declaration` - The corelib function declaration of `array_append<T>`.
///
/// # 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<T>>(array: Array<T>, val: T) -> Array<T>
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")));
}
}
2 changes: 2 additions & 0 deletions core/src/sierra/corelib_functions/array/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod append;
pub mod new;
40 changes: 40 additions & 0 deletions core/src/sierra/corelib_functions/array/new.rs
Original file line number Diff line number Diff line change
@@ -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<T>` operation.
///
/// # Arguments
///
/// * `libfunc_declaration` - The corelib function declaration of `array_new<T>`.
///
/// # 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));
}
}
19 changes: 9 additions & 10 deletions core/src/sierra/corelib_functions/math/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));

Expand All @@ -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));

Expand Down
2 changes: 1 addition & 1 deletion core/src/sierra/corelib_functions/memory/store_temp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
1 change: 1 addition & 0 deletions core/src/sierra/corelib_functions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod array;
pub mod boxes;
pub mod functions;
pub mod math;
Expand Down
7 changes: 5 additions & 2 deletions core/src/sierra/process/corelib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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
Expand All @@ -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`
Expand All @@ -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<T>` to `T`.
"unbox" => self.unbox(libfunc_declaration),
_ => debug!(libfunc_name, "not implemented"),
Expand Down
2 changes: 1 addition & 1 deletion core/src/sierra/process/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
10 changes: 10 additions & 0 deletions core/src/sierra/process/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>
"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"),
}
}
Expand Down
45 changes: 45 additions & 0 deletions core/src/sierra/types/array.rs
Original file line number Diff line number Diff line change
@@ -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<T> 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")
}
};
}
}
1 change: 1 addition & 0 deletions core/src/sierra/types/builtins/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod range_check;
13 changes: 13 additions & 0 deletions core/src/sierra/types/builtins/range_check.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
3 changes: 3 additions & 0 deletions core/src/sierra/types/math/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod felt;
pub mod non_zero;
pub mod sierra_u32;
13 changes: 13 additions & 0 deletions core/src/sierra/types/math/sierra_u32.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
7 changes: 5 additions & 2 deletions core/src/sierra/types/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
39 changes: 39 additions & 0 deletions core/src/sierra/types/sierra_enum.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading