Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added conversion from span to fixed sized array. #5582

Merged
merged 1 commit into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
78 changes: 62 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,65 @@ 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.
/// Any user type with such members is considered a tuple.
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)>>);
Loading