Skip to content

Commit

Permalink
Added conversion from span to fixed sized array.
Browse files Browse the repository at this point in the history
commit-id:78f53cb7
  • Loading branch information
orizi committed May 19, 2024
1 parent c882758 commit 7814bab
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
Array(libfunc) => match libfunc {
ArrayConcreteLibfunc::New(_) => vec![ApChange::Known(1)],
ArrayConcreteLibfunc::SpanFromTuple(_) => vec![ApChange::Known(0)],
ArrayConcreteLibfunc::TupleFromSpan(_) => vec![ApChange::Known(2), ApChange::Known(2)],
ArrayConcreteLibfunc::Append(_) => vec![ApChange::Known(0)],
ArrayConcreteLibfunc::PopFront(_)
| ArrayConcreteLibfunc::PopFrontConsume(_)
Expand Down
3 changes: 3 additions & 0 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ pub fn core_libfunc_cost(
Array(libfunc) => match libfunc {
ArrayConcreteLibfunc::New(_) => vec![ConstCost::steps(1).into()],
ArrayConcreteLibfunc::SpanFromTuple(_) => vec![ConstCost::steps(0).into()],
ArrayConcreteLibfunc::TupleFromSpan(_) => {
vec![ConstCost::steps(3).into(), ConstCost::steps(3).into()]
}
ArrayConcreteLibfunc::Append(libfunc) => {
vec![ConstCost::steps(info_provider.type_size(&libfunc.ty) as i32).into()]
}
Expand Down
34 changes: 32 additions & 2 deletions crates/cairo-lang-sierra-to-casm/src/invocations/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub fn build(
match libfunc {
ArrayConcreteLibfunc::New(_) => build_array_new(builder),
ArrayConcreteLibfunc::SpanFromTuple(libfunc) => build_span_from_tuple(builder, &libfunc.ty),
ArrayConcreteLibfunc::TupleFromSpan(libfunc) => build_tuple_from_span(builder, &libfunc.ty),
ArrayConcreteLibfunc::Append(_) => build_array_append(builder),
ArrayConcreteLibfunc::PopFront(libfunc)
| ArrayConcreteLibfunc::SnapshotPopFront(libfunc) => {
Expand Down Expand Up @@ -51,8 +52,8 @@ fn build_array_new(
))
}

// Builds instructions for converting a box of a struct containing only the same type as members
// into a span of that type.
/// Builds instructions for converting a box of a struct containing only the same type as members
/// into a span of that type.
fn build_span_from_tuple(
builder: CompiledInvocationBuilder<'_>,
ty: &ConcreteTypeId,
Expand All @@ -78,6 +79,35 @@ fn build_span_from_tuple(
))
}

/// Builds instructions for converting a span of a type into a box of a struct containing only
/// members of that same type.
fn build_tuple_from_span(
builder: CompiledInvocationBuilder<'_>,
ty: &ConcreteTypeId,
) -> Result<CompiledInvocation, InvocationError> {
let [arr_start, arr_end] = builder.try_get_refs::<1>()?[0].try_unpack()?;
let full_struct_size = builder.program_info.type_sizes[ty];

let mut casm_builder = CasmBuilder::default();

add_input_variables! {casm_builder,
deref arr_start;
deref arr_end;
};
casm_build_extend! {casm_builder,
const success_span_size = full_struct_size;
tempvar actual_length = arr_end - arr_start;
tempvar diff = actual_length - success_span_size;
jump Failure if diff != 0;
};
let failure_handle = get_non_fallthrough_statement_id(&builder);
Ok(builder.build_from_casm_builder(
casm_builder,
[("Fallthrough", &[&[arr_start]], None), ("Failure", &[], Some(failure_handle))],
Default::default(),
))
}

/// Handles a Sierra statement for appending an element to an array.
fn build_array_append(
builder: CompiledInvocationBuilder<'_>,
Expand Down
77 changes: 61 additions & 16 deletions crates/cairo-lang-sierra/src/extensions/modules/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ define_libfunc_hierarchy! {
pub enum ArrayLibfunc {
New(ArrayNewLibfunc),
SpanFromTuple(SpanFromTupleLibfunc),
TupleFromSpan(TupleFromSpanLibfunc),
Append(ArrayAppendLibfunc),
PopFront(ArrayPopFrontLibfunc),
PopFrontConsume(ArrayPopFrontConsumeLibfunc),
Expand Down Expand Up @@ -96,24 +97,10 @@ impl SignatureAndTypeGenericLibfunc for SpanFromTupleLibfuncWrapped {
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let struct_type = StructConcreteType::try_from_concrete_type(context, &ty)?;
if struct_type.info.zero_sized {
return Err(SpecializationError::UnsupportedGenericArg);
}

let member_type =
struct_type.members.first().ok_or(SpecializationError::UnsupportedGenericArg)?;
// Validate all members are of the same type.
for member in &struct_type.members {
if member != member_type {
return Err(SpecializationError::UnsupportedGenericArg);
}
}

let input_ty = box_ty(context, snapshot_ty(context, ty)?)?;
let member_type = validate_tuple_and_fetch_ty(context, &ty)?;

Ok(LibfuncSignature::new_non_branch(
vec![input_ty],
vec![box_ty(context, snapshot_ty(context, ty)?)?],
vec![OutputVarInfo {
ty: snapshot_ty(
context,
Expand All @@ -130,6 +117,64 @@ impl SignatureAndTypeGenericLibfunc for SpanFromTupleLibfuncWrapped {

pub type SpanFromTupleLibfunc = WrapSignatureAndTypeGenericLibfunc<SpanFromTupleLibfuncWrapped>;

/// Libfunc for creating a box of struct of members of the same type from a span.
#[derive(Default)]
pub struct TupleFromSpanLibfuncWrapped;
impl SignatureAndTypeGenericLibfunc for TupleFromSpanLibfuncWrapped {
const STR_ID: &'static str = "tuple_from_span";

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let member_type = validate_tuple_and_fetch_ty(context, &ty)?;

Ok(LibfuncSignature {
param_signatures: vec![ParamSignature::new(snapshot_ty(
context,
context.get_wrapped_concrete_type(ArrayType::id(), member_type)?,
)?)],
branch_signatures: vec![
BranchSignature {
vars: vec![OutputVarInfo {
ty: snapshot_ty(context, box_ty(context, ty)?)?,
ref_info: OutputVarReferenceInfo::PartialParam { param_idx: 0 },
}],
ap_change: SierraApChange::Known { new_vars_only: false },
},
BranchSignature {
vars: vec![],
ap_change: SierraApChange::Known { new_vars_only: false },
},
],
fallthrough: Some(0),
})
}
}

/// Validates that the given type is a tuple with all members of the same type, and returns the type
/// of the members.
fn validate_tuple_and_fetch_ty(
context: &dyn SignatureSpecializationContext,
ty: &ConcreteTypeId,
) -> Result<ConcreteTypeId, SpecializationError> {
let struct_type = StructConcreteType::try_from_concrete_type(context, ty)?;
if struct_type.info.zero_sized {
return Err(SpecializationError::UnsupportedGenericArg);
}
let mut members = struct_type.members.into_iter();
let member_type = members.next().ok_or(SpecializationError::UnsupportedGenericArg)?;
for member in members {
if member != member_type {
return Err(SpecializationError::UnsupportedGenericArg);
}
}
Ok(member_type)
}

pub type TupleFromSpanLibfunc = WrapSignatureAndTypeGenericLibfunc<TupleFromSpanLibfuncWrapped>;

/// Libfunc for getting the length of the array.
#[derive(Default)]
pub struct ArrayLenLibfuncWrapped {}
Expand Down
1 change: 1 addition & 0 deletions crates/cairo-lang-sierra/src/simulation/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ pub fn simulate<
}
}
Array(ArrayConcreteLibfunc::SpanFromTuple(_)) => todo!(),
Array(ArrayConcreteLibfunc::TupleFromSpan(_)) => todo!(),
Array(ArrayConcreteLibfunc::Append(_)) => match &inputs[..] {
[CoreValue::Array(_), _] => {
let mut iter = inputs.into_iter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
"struct_construct",
"struct_deconstruct",
"struct_snapshot_deconstruct",
"tuple_from_span",
"u128_byte_reverse",
"u128_const",
"u128_eq",
Expand Down
57 changes: 57 additions & 0 deletions tests/e2e_test_data/libfuncs/array
Original file line number Diff line number Diff line change
Expand Up @@ -890,3 +890,60 @@ store_temp<Snapshot<Array<felt252>>>([1]) -> ([1]); // 1
return([1]); // 2

test::foo@0([0]: Box<Tuple<felt252, felt252, felt252>>) -> (Snapshot<Array<felt252>>);

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

//! > tuple_from_span libfunc

//! > test_runner_name
SmallE2ETestRunner

//! > cairo
extern fn tuple_from_span<T>(span: @Array<felt252>) -> Option<@Box<T>> nopanic;

fn foo(span: @Array<felt252>) -> Option<@Box<(felt252, felt252, felt252)>> {
tuple_from_span(span)
}

//! > casm
[fp + -3] = [ap + 0] + [fp + -4], ap++;
[ap + -1] = [ap + 0] + 3, ap++;
jmp rel 6 if [ap + -1] != 0;
[ap + 0] = 0, ap++;
[ap + 0] = [fp + -4], ap++;
ret;
[ap + 0] = 1, ap++;
[ap + 0] = 0, ap++;
ret;

//! > function_costs
test::foo: OrderedHashMap({Const: 500})

//! > sierra_code
type Array<felt252> = Array<felt252> [storable: true, drop: true, dup: false, zero_sized: false];
type Snapshot<Array<felt252>> = Snapshot<Array<felt252>> [storable: true, drop: true, dup: true, zero_sized: false];
type Unit = Struct<ut@Tuple> [storable: true, drop: true, dup: true, zero_sized: true];
type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false];
type Box<Tuple<felt252, felt252, felt252>> = Box<Tuple<felt252, felt252, felt252>> [storable: true, drop: true, dup: true, zero_sized: false];
type core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>> = Enum<ut@core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>, Box<Tuple<felt252, felt252, felt252>>, Unit> [storable: true, drop: true, dup: true, zero_sized: false];
type Tuple<felt252, felt252, felt252> = Struct<ut@Tuple, felt252, felt252, felt252> [storable: true, drop: true, dup: true, zero_sized: false];

libfunc tuple_from_span<Tuple<felt252, felt252, felt252>> = tuple_from_span<Tuple<felt252, felt252, felt252>>;
libfunc branch_align = branch_align;
libfunc enum_init<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>, 0> = enum_init<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>, 0>;
libfunc store_temp<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>> = store_temp<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>>;
libfunc struct_construct<Unit> = struct_construct<Unit>;
libfunc enum_init<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>, 1> = enum_init<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>, 1>;

tuple_from_span<Tuple<felt252, felt252, felt252>>([0]) { fallthrough([1]) 5() }; // 0
branch_align() -> (); // 1
enum_init<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>, 0>([1]) -> ([2]); // 2
store_temp<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>>([2]) -> ([2]); // 3
return([2]); // 4
branch_align() -> (); // 5
struct_construct<Unit>() -> ([3]); // 6
enum_init<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>, 1>([3]) -> ([4]); // 7
store_temp<core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>>([4]) -> ([4]); // 8
return([4]); // 9

test::foo@0([0]: Snapshot<Array<felt252>>) -> (core::option::Option::<@core::box::Box::<(core::felt252, core::felt252, core::felt252)>>);

0 comments on commit 7814bab

Please sign in to comment.