Skip to content
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
36 changes: 20 additions & 16 deletions crates/cairo-lang-semantic/src/usage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,8 @@ impl<'db> Usage<'db> {
/// Removes usage that was introduced current block and usage that is already covered
/// by containing variables.
pub fn finalize_as_scope(&mut self) {
// Prune introductions from usages.
for member_path in prune_and_get_candidates(&mut self.usage, |k| {
self.introductions.contains(&k.base_var())
}) {
// Prune usages that are members of other usages.
let mut current_path = &member_path;
while let MemberPath::Member { parent, .. } = current_path {
current_path = parent.as_ref();
if self.usage.contains_key(current_path) {
self.usage.swap_remove(&member_path);
break;
}
}
}
// Prune usages and introdictions from snap_usage.
// Prune usages and introdictions from snap_usage. Runs before the `changes` pass so that
// the promotion below always promotes the top-most snapshotted ancestor.
for member_path in prune_and_get_candidates(&mut self.snap_usage, |k| {
self.usage.contains_key(k) || self.introductions.contains(&k.base_var())
}) {
Expand All @@ -114,7 +101,9 @@ impl<'db> Usage<'db> {
self.introductions.contains(&k.base_var())
}) {
// Prune changes that are members of other changes.
// Also if a child is changed and its parent is used, then we change the parent.
// Also if a child is changed and an ancestor is used as a snapshot, promote the
// ancestor into `usage`/`changes` so the whole ancestor is read and written back,
// instead of returning the child of a stale snapshot.
// TODO(TomerStarkware): Deconstruct the parent, and snap_use other members.
let mut current_path = &member_path;
while let MemberPath::Member { parent, .. } = current_path {
Expand All @@ -133,6 +122,21 @@ impl<'db> Usage<'db> {
}
}
}
// Prune introductions from usages. Runs after the `changes` pass so that members of an
// ancestor promoted above are pruned as well.
for member_path in prune_and_get_candidates(&mut self.usage, |k| {
self.introductions.contains(&k.base_var())
}) {
// Prune usages that are members of other usages.
let mut current_path = &member_path;
while let MemberPath::Member { parent, .. } = current_path {
current_path = parent.as_ref();
if self.usage.contains_key(current_path) {
self.usage.swap_remove(&member_path);
break;
}
}
}
}
}

Expand Down
265 changes: 265 additions & 0 deletions tests/e2e_test_data/libfuncs/casm_run_sanity
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,268 @@ test::fib@F0([0]: RangeCheck, [1]: GasBuiltin, [2]: felt252) -> (RangeCheck, Gas
test::fib[116-219]@F1([0]: RangeCheck, [1]: GasBuiltin, [2]: felt252, [3]: felt252, [4]: felt252, [5]: felt252) -> (RangeCheck, GasBuiltin, core::panics::PanicResult::<(core::felt252, core::felt252, core::felt252, ())>);
core::panic_with_const_felt252::<375233589013918064796019>@F2() -> (Tuple<core::panics::Panic, Array<felt252>>);
core::panic_with_felt252@F3([0]: felt252) -> (Tuple<core::panics::Panic, Array<felt252>>);

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

//! > Loop that reads an ancestor as a snapshot (@a.b) while also mutating a nested field (a.b.c)

//! > must preserve the mutation. When `finalize_as_scope` promotes the snapped ancestor into

//! > usage/changes, the later usage-pruning pass subsumes its descendants so the loop reads and writes back the whole

//! > ancestor. For x=2 the result is 1*2*2=4 (0x4).

//! > test_runner_name
SmallE2ETestRunner(test_data_function: loop_snap_mut, test_data_input: 2, test_data_output: 4)

//! > cairo_code
#[derive(Drop)]
struct Inner {
c: felt252,
}
#[derive(Drop)]
struct Outer {
b: Inner,
}

fn loop_snap_mut(x: felt252) -> felt252 {
let mut a = Outer { b: Inner { c: 1 } };
let mut i: felt252 = 0;
loop {
if i == x {
break;
}
let _snap = @a.b;
a.b.c = a.b.c * 2;
i = i + 1;
}
a.b.c
}

//! > casm
[ap + 0] = [fp + -5], ap++;
[ap + 0] = [fp + -4], ap++;
[ap + 0] = 1, ap++;
[ap + 0] = 0, ap++;
[ap + 0] = [fp + -3], ap++;
call rel 19;
jmp rel 10 if [ap + -3] != 0;
[ap + 0] = [ap + -5], ap++;
[ap + 0] = [ap + -5], ap++;
[ap + 0] = 0, ap++;
[ap + 0] = 0, ap++;
[ap + 0] = [ap + -6], ap++;
ret;
[ap + 0] = [ap + -5], ap++;
[ap + 0] = [ap + -5], ap++;
[ap + 0] = 1, ap++;
[ap + 0] = [ap + -5], ap++;
[ap + 0] = [ap + -5], ap++;
ret;
%{ memory[ap + 0] = 1270 <= memory[fp + -6] %}
jmp rel 7 if [ap + 0] != 0, ap++;
[ap + 0] = [fp + -6] + 340282366920938463463374607431768210186, ap++;
[ap + -1] = [[fp + -7] + 0];
jmp rel 28;
[fp + -6] = [ap + 0] + 1270, ap++;
[ap + -1] = [[fp + -7] + 0];
[fp + -4] = [ap + 0] + [fp + -3], ap++;
jmp rel 11 if [ap + -1] != 0;
[ap + 0] = [fp + -7] + 1, ap++;
[ap + 0] = [ap + -3] + 2070, ap++;
[ap + 0] = 0, ap++;
[ap + 0] = [fp + -5], ap++;
[ap + 0] = [fp + -4], ap++;
ret;
[ap + 0] = [fp + -7] + 1, ap++;
[ap + 0] = [ap + -3], ap++;
[ap + 0] = [fp + -5] * 2, ap++;
[ap + 0] = [fp + -4] + 1, ap++;
[ap + 0] = [fp + -3], ap++;
call rel -30;
ret;
call rel 10;
[ap + 0] = [fp + -7] + 1, ap++;
[ap + 0] = [fp + -6], ap++;
[ap + 0] = 1, ap++;
[ap + 0] = [ap + -5], ap++;
[ap + 0] = [ap + -5], ap++;
ret;
[ap + 0] = 375233589013918064796019, ap++;
call rel 3;
ret;
%{ memory[ap + 0] = segments.add() %}
ap += 1;
[fp + -3] = [[ap + -1] + 0];
[ap + 0] = [ap + -1], ap++;
[ap + 0] = [ap + -2] + 1, ap++;
ret;

//! > function_costs
test::loop_snap_mut: SmallOrderedMap({Const: 3170})
test::loop_snap_mut[116-254]: SmallOrderedMap({Const: 1870})
core::panic_with_const_felt252::<375233589013918064796019>: SmallOrderedMap({Const: 700})
core::panic_with_felt252: SmallOrderedMap({Const: 400})

//! > sierra_code
type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false];
type core::panics::Panic = Struct<ut@core::panics::Panic> [storable: true, drop: true, dup: true, zero_sized: true];
type Array<felt252> = Array<felt252> [storable: true, drop: true, dup: false, zero_sized: false];
type Const<felt252, 375233589013918064796019> = Const<felt252, 375233589013918064796019> [storable: false, drop: false, dup: false, zero_sized: false];
type Const<felt252, 2> = Const<felt252, 2> [storable: false, drop: false, dup: false, zero_sized: false];
type NonZero<felt252> = NonZero<felt252> [storable: true, drop: true, dup: true, zero_sized: false];
type Tuple<felt252> = Struct<ut@Tuple, felt252> [storable: true, drop: true, dup: true, zero_sized: false];
type Tuple<core::panics::Panic, Array<felt252>> = Struct<ut@Tuple, core::panics::Panic, Array<felt252>> [storable: true, drop: true, dup: false, zero_sized: false];
type core::panics::PanicResult::<(core::felt252,)> = Enum<ut@core::panics::PanicResult::<(core::felt252,)>, Tuple<felt252>, Tuple<core::panics::Panic, Array<felt252>>> [storable: true, drop: true, dup: false, zero_sized: false];
type Unit = Struct<ut@Tuple> [storable: true, drop: true, dup: true, zero_sized: true];
type test::Inner = Struct<ut@test::Inner, felt252> [storable: true, drop: true, dup: true, zero_sized: false];
type Tuple<test::Inner, felt252, Unit> = Struct<ut@Tuple, test::Inner, felt252, Unit> [storable: true, drop: true, dup: true, zero_sized: false];
type core::panics::PanicResult::<(test::Inner, core::felt252, ())> = Enum<ut@core::panics::PanicResult::<(test::Inner, core::felt252, ())>, Tuple<test::Inner, felt252, Unit>, Tuple<core::panics::Panic, Array<felt252>>> [storable: true, drop: true, dup: false, zero_sized: false];
type GasBuiltin = GasBuiltin [storable: true, drop: false, dup: false, zero_sized: false];
type RangeCheck = RangeCheck [storable: true, drop: false, dup: false, zero_sized: false];
type Const<felt252, 0> = Const<felt252, 0> [storable: false, drop: false, dup: false, zero_sized: false];
type Const<felt252, 1> = Const<felt252, 1> [storable: false, drop: false, dup: false, zero_sized: false];

libfunc disable_ap_tracking = disable_ap_tracking;
libfunc const_as_immediate<Const<felt252, 1>> = const_as_immediate<Const<felt252, 1>>;
libfunc const_as_immediate<Const<felt252, 0>> = const_as_immediate<Const<felt252, 0>>;
libfunc snapshot_take<felt252> = snapshot_take<felt252>;
libfunc drop<felt252> = drop<felt252>;
libfunc struct_construct<test::Inner> = struct_construct<test::Inner>;
libfunc store_temp<RangeCheck> = store_temp<RangeCheck>;
libfunc store_temp<GasBuiltin> = store_temp<GasBuiltin>;
libfunc store_temp<test::Inner> = store_temp<test::Inner>;
libfunc store_temp<felt252> = store_temp<felt252>;
libfunc function_call<user@test::loop_snap_mut[116-254]> = function_call<user@test::loop_snap_mut[116-254]>;
libfunc enum_match<core::panics::PanicResult::<(test::Inner, core::felt252, ())>> = enum_match<core::panics::PanicResult::<(test::Inner, core::felt252, ())>>;
libfunc branch_align = branch_align;
libfunc redeposit_gas = redeposit_gas;
libfunc struct_deconstruct<Tuple<test::Inner, felt252, Unit>> = struct_deconstruct<Tuple<test::Inner, felt252, Unit>>;
libfunc drop<Unit> = drop<Unit>;
libfunc struct_deconstruct<test::Inner> = struct_deconstruct<test::Inner>;
libfunc struct_construct<Tuple<felt252>> = struct_construct<Tuple<felt252>>;
libfunc enum_init<core::panics::PanicResult::<(core::felt252,)>, 0> = enum_init<core::panics::PanicResult::<(core::felt252,)>, 0>;
libfunc store_temp<core::panics::PanicResult::<(core::felt252,)>> = store_temp<core::panics::PanicResult::<(core::felt252,)>>;
libfunc enum_init<core::panics::PanicResult::<(core::felt252,)>, 1> = enum_init<core::panics::PanicResult::<(core::felt252,)>, 1>;
libfunc withdraw_gas = withdraw_gas;
libfunc dup<felt252> = dup<felt252>;
libfunc rename<felt252> = rename<felt252>;
libfunc felt252_sub = felt252_sub;
libfunc felt252_is_zero = felt252_is_zero;
libfunc struct_construct<Unit> = struct_construct<Unit>;
libfunc struct_construct<Tuple<test::Inner, felt252, Unit>> = struct_construct<Tuple<test::Inner, felt252, Unit>>;
libfunc enum_init<core::panics::PanicResult::<(test::Inner, core::felt252, ())>, 0> = enum_init<core::panics::PanicResult::<(test::Inner, core::felt252, ())>, 0>;
libfunc store_temp<core::panics::PanicResult::<(test::Inner, core::felt252, ())>> = store_temp<core::panics::PanicResult::<(test::Inner, core::felt252, ())>>;
libfunc drop<NonZero<felt252>> = drop<NonZero<felt252>>;
libfunc const_as_immediate<Const<felt252, 2>> = const_as_immediate<Const<felt252, 2>>;
libfunc felt252_mul = felt252_mul;
libfunc felt252_add = felt252_add;
libfunc drop<test::Inner> = drop<test::Inner>;
libfunc function_call<user@core::panic_with_const_felt252::<375233589013918064796019>> = function_call<user@core::panic_with_const_felt252::<375233589013918064796019>>;
libfunc enum_init<core::panics::PanicResult::<(test::Inner, core::felt252, ())>, 1> = enum_init<core::panics::PanicResult::<(test::Inner, core::felt252, ())>, 1>;
libfunc const_as_immediate<Const<felt252, 375233589013918064796019>> = const_as_immediate<Const<felt252, 375233589013918064796019>>;
libfunc function_call<user@core::panic_with_felt252> = function_call<user@core::panic_with_felt252>;
libfunc array_new<felt252> = array_new<felt252>;
libfunc array_append<felt252> = array_append<felt252>;
libfunc struct_construct<core::panics::Panic> = struct_construct<core::panics::Panic>;
libfunc struct_construct<Tuple<core::panics::Panic, Array<felt252>>> = struct_construct<Tuple<core::panics::Panic, Array<felt252>>>;
libfunc store_temp<Tuple<core::panics::Panic, Array<felt252>>> = store_temp<Tuple<core::panics::Panic, Array<felt252>>>;

F0:
disable_ap_tracking() -> ();
const_as_immediate<Const<felt252, 1>>() -> ([3]);
const_as_immediate<Const<felt252, 0>>() -> ([4]);
snapshot_take<felt252>([2]) -> ([5], [6]);
drop<felt252>([5]) -> ();
struct_construct<test::Inner>([3]) -> ([7]);
store_temp<RangeCheck>([0]) -> ([0]);
store_temp<GasBuiltin>([1]) -> ([1]);
store_temp<test::Inner>([7]) -> ([7]);
store_temp<felt252>([4]) -> ([4]);
store_temp<felt252>([6]) -> ([6]);
function_call<user@test::loop_snap_mut[116-254]>([0], [1], [7], [4], [6]) -> ([8], [9], [10]);
enum_match<core::panics::PanicResult::<(test::Inner, core::felt252, ())>>([10]) { fallthrough([11]) F0_B0([12]) };
branch_align() -> ();
redeposit_gas([9]) -> ([13]);
struct_deconstruct<Tuple<test::Inner, felt252, Unit>>([11]) -> ([14], [15], [16]);
drop<felt252>([15]) -> ();
drop<Unit>([16]) -> ();
struct_deconstruct<test::Inner>([14]) -> ([17]);
struct_construct<Tuple<felt252>>([17]) -> ([18]);
enum_init<core::panics::PanicResult::<(core::felt252,)>, 0>([18]) -> ([19]);
store_temp<RangeCheck>([8]) -> ([8]);
store_temp<GasBuiltin>([13]) -> ([13]);
store_temp<core::panics::PanicResult::<(core::felt252,)>>([19]) -> ([19]);
return([8], [13], [19]);
F0_B0:
branch_align() -> ();
enum_init<core::panics::PanicResult::<(core::felt252,)>, 1>([12]) -> ([20]);
store_temp<RangeCheck>([8]) -> ([8]);
store_temp<GasBuiltin>([9]) -> ([9]);
store_temp<core::panics::PanicResult::<(core::felt252,)>>([20]) -> ([20]);
return([8], [9], [20]);
F1:
disable_ap_tracking() -> ();
withdraw_gas([0], [1]) { fallthrough([5], [6]) F1_B1([7], [8]) };
branch_align() -> ();
dup<felt252>([4]) -> ([4], [9]);
rename<felt252>([9]) -> ([10]);
dup<felt252>([3]) -> ([3], [11]);
felt252_sub([11], [10]) -> ([12]);
store_temp<felt252>([12]) -> ([12]);
felt252_is_zero([12]) { fallthrough() F1_B0([13]) };
branch_align() -> ();
drop<felt252>([4]) -> ();
redeposit_gas([6]) -> ([14]);
struct_construct<Unit>() -> ([15]);
struct_construct<Tuple<test::Inner, felt252, Unit>>([2], [3], [15]) -> ([16]);
enum_init<core::panics::PanicResult::<(test::Inner, core::felt252, ())>, 0>([16]) -> ([17]);
store_temp<RangeCheck>([5]) -> ([5]);
store_temp<GasBuiltin>([14]) -> ([14]);
store_temp<core::panics::PanicResult::<(test::Inner, core::felt252, ())>>([17]) -> ([17]);
return([5], [14], [17]);
F1_B0:
branch_align() -> ();
drop<NonZero<felt252>>([13]) -> ();
redeposit_gas([6]) -> ([18]);
struct_deconstruct<test::Inner>([2]) -> ([19]);
const_as_immediate<Const<felt252, 2>>() -> ([20]);
felt252_mul([19], [20]) -> ([21]);
const_as_immediate<Const<felt252, 1>>() -> ([22]);
felt252_add([3], [22]) -> ([23]);
struct_construct<test::Inner>([21]) -> ([24]);
store_temp<RangeCheck>([5]) -> ([5]);
store_temp<GasBuiltin>([18]) -> ([18]);
store_temp<test::Inner>([24]) -> ([24]);
store_temp<felt252>([23]) -> ([23]);
store_temp<felt252>([4]) -> ([4]);
function_call<user@test::loop_snap_mut[116-254]>([5], [18], [24], [23], [4]) -> ([25], [26], [27]);
return([25], [26], [27]);
F1_B1:
branch_align() -> ();
drop<felt252>([4]) -> ();
drop<felt252>([3]) -> ();
drop<test::Inner>([2]) -> ();
function_call<user@core::panic_with_const_felt252::<375233589013918064796019>>() -> ([28]);
enum_init<core::panics::PanicResult::<(test::Inner, core::felt252, ())>, 1>([28]) -> ([29]);
store_temp<RangeCheck>([7]) -> ([7]);
store_temp<GasBuiltin>([8]) -> ([8]);
store_temp<core::panics::PanicResult::<(test::Inner, core::felt252, ())>>([29]) -> ([29]);
return([7], [8], [29]);
F2:
const_as_immediate<Const<felt252, 375233589013918064796019>>() -> ([0]);
store_temp<felt252>([0]) -> ([0]);
function_call<user@core::panic_with_felt252>([0]) -> ([1]);
return([1]);
F3:
array_new<felt252>() -> ([1]);
array_append<felt252>([1], [0]) -> ([2]);
struct_construct<core::panics::Panic>() -> ([3]);
struct_construct<Tuple<core::panics::Panic, Array<felt252>>>([3], [2]) -> ([4]);
store_temp<Tuple<core::panics::Panic, Array<felt252>>>([4]) -> ([4]);
return([4]);

test::loop_snap_mut@F0([0]: RangeCheck, [1]: GasBuiltin, [2]: felt252) -> (RangeCheck, GasBuiltin, core::panics::PanicResult::<(core::felt252,)>);
test::loop_snap_mut[116-254]@F1([0]: RangeCheck, [1]: GasBuiltin, [2]: test::Inner, [3]: felt252, [4]: felt252) -> (RangeCheck, GasBuiltin, core::panics::PanicResult::<(test::Inner, core::felt252, ())>);
core::panic_with_const_felt252::<375233589013918064796019>@F2() -> (Tuple<core::panics::Panic, Array<felt252>>);
core::panic_with_felt252@F3([0]: felt252) -> (Tuple<core::panics::Panic, Array<felt252>>);
Loading