Skip to content

Commit 48105ae

Browse files
(optimization): Reboxing also applied on Box of snapshots
1 parent dbe1bcf commit 48105ae

File tree

4 files changed

+127
-32
lines changed

4 files changed

+127
-32
lines changed

crates/cairo-lang-lowering/src/optimizations/reboxing.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::rc::Rc;
88
use cairo_lang_filesystem::flag::flag_future_sierra;
99
use cairo_lang_semantic::helper::ModuleHelper;
1010
use cairo_lang_semantic::items::structure::StructSemantic;
11-
use cairo_lang_semantic::types::{TypesSemantic, peel_snapshots};
11+
use cairo_lang_semantic::types::{TypesSemantic, peel_snapshots, wrap_in_snapshots};
1212
use cairo_lang_semantic::{ConcreteTypeId, GenericArgumentId, TypeId, TypeLongId};
1313
use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
1414
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
@@ -255,7 +255,7 @@ fn create_struct_boxed_deconstruct_call<'db>(
255255
old_stmt: &Statement<'db>,
256256
) -> Option<Statement<'db>> {
257257
// TODO(eytan-starkware): Accept a collection of vars to create a box of. A single call to
258-
// struct_boxed_deconstruct can be created for multiple vars. When creating multivars
258+
// struct_boxed_deconstruct can be created for multiple vars. When creating multivars
259259
// we need to put creation at a dominating point.
260260

261261
let boxed_struct_ty = variables[boxed_struct_var].ty;
@@ -277,26 +277,25 @@ fn create_struct_boxed_deconstruct_call<'db>(
277277
}
278278
let (n_snapshots, struct_ty) = peel_snapshots(db, *inner_ty);
279279

280-
// TODO(eytan-starkware): Support snapshots of structs in reboxing optimization.
281-
// Currently we give up if the struct is wrapped in snapshots.
282-
if n_snapshots > 0 {
283-
trace!("Skipping reboxing for snapshotted struct (n_snapshots={})", n_snapshots);
284-
return None;
285-
}
286-
287280
trace!("Extracted struct or tuple type: {:?}", struct_ty);
288281

289282
// Get the type info to determine number of members
290283
let (num_members, member_types): (usize, Vec<TypeId<'_>>) = match struct_ty {
291284
TypeLongId::Concrete(ConcreteTypeId::Struct(struct_id)) => {
292285
let members = db.concrete_struct_members(struct_id).ok()?;
293286
let num = members.len();
294-
let types = members.iter().map(|(_, member)| member.ty).collect();
287+
let types = members
288+
.iter()
289+
.map(|(_, member)| wrap_in_snapshots(db, member.ty, n_snapshots))
290+
.collect();
295291
(num, types)
296292
}
297293
TypeLongId::Tuple(inner_types) => {
298294
let num = inner_types.len();
299-
(num, inner_types)
295+
(
296+
num,
297+
inner_types.into_iter().map(|ty| wrap_in_snapshots(db, ty, n_snapshots)).collect(),
298+
)
300299
}
301300
_ => {
302301
trace!("Unsupported type for reboxing: {:?}", struct_ty);

crates/cairo-lang-lowering/src/optimizations/test_data/reboxing

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ blk0 (root):
225225
Statements:
226226
(v1: @test::Data) <- core::box::unbox::<@test::Data>(v0)
227227
(v2: @test::NonCopy, v3: @core::felt252) <- struct_destructure(v1)
228-
(v4: core::box::Box::<@test::NonCopy>) <- core::box::into_box::<@test::NonCopy>(v2)
228+
(v4: core::box::Box::<@test::NonCopy>, v5: core::box::Box::<@core::felt252>) <- struct_destructure(v0)
229229
End:
230230
Return(v4)
231231

@@ -779,3 +779,68 @@ Statements:
779779
(v5: (core::box::Box::<core::felt252>, test::NonDrop)) <- struct_construct(v4, v3)
780780
End:
781781
Return(v5)
782+
783+
//! > ==========================================================================
784+
785+
//! > Test reboxing with snapshot of non-drop struct and member
786+
787+
//! > test_runner_name
788+
test_reboxing_analysis
789+
790+
//! > function_name
791+
main
792+
793+
//! > module_code
794+
use core::box::BoxTrait;
795+
796+
#[derive(Copy)]
797+
struct NonDrop {
798+
b: felt252,
799+
}
800+
801+
#[derive(Copy)]
802+
struct A {
803+
a: felt252,
804+
non_drop: NonDrop,
805+
}
806+
807+
//! > function_code
808+
fn main(a: @A) -> (Box<@felt252>, @NonDrop) {
809+
let boxed = BoxTrait::new(a);
810+
let unboxed = boxed.unbox();
811+
let member_a = unboxed.a;
812+
(BoxTrait::new(member_a), unboxed.non_drop)
813+
}
814+
815+
//! > semantic_diagnostics
816+
817+
//! > lowering_diagnostics
818+
819+
//! > candidates
820+
v5
821+
822+
//! > before
823+
Parameters: v0: @test::A
824+
blk0 (root):
825+
Statements:
826+
(v1: core::box::Box::<@test::A>) <- core::box::into_box::<@test::A>(v0)
827+
(v2: @test::A) <- core::box::unbox::<@test::A>(v1)
828+
(v3: @core::felt252, v4: @test::NonDrop) <- struct_destructure(v2)
829+
(v5: core::box::Box::<@core::felt252>) <- core::box::into_box::<@core::felt252>(v3)
830+
(v6: @core::felt252, v7: @test::NonDrop) <- struct_destructure(v2)
831+
(v8: (core::box::Box::<@core::felt252>, @test::NonDrop)) <- struct_construct(v5, v7)
832+
End:
833+
Return(v8)
834+
835+
//! > after
836+
Parameters: v0: @test::A
837+
blk0 (root):
838+
Statements:
839+
(v1: core::box::Box::<@test::A>) <- core::box::into_box::<@test::A>(v0)
840+
(v2: @test::A) <- core::box::unbox::<@test::A>(v1)
841+
(v3: @core::felt252, v4: @test::NonDrop) <- struct_destructure(v2)
842+
(v5: core::box::Box::<@core::felt252>, v9: core::box::Box::<@test::NonDrop>) <- struct_destructure(v1)
843+
(v6: @core::felt252, v7: @test::NonDrop) <- struct_destructure(v2)
844+
(v8: (core::box::Box::<@core::felt252>, @test::NonDrop)) <- struct_construct(v5, v7)
845+
End:
846+
Return(v8)

crates/cairo-lang-sierra-generator/src/function_generator_test_data/struct

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -118,18 +118,16 @@ struct A {
118118

119119
//! > lowering_diagnostics
120120

121-
//! > TODO(eytan-starkware): We want reboxing to apply to sierra in the future,
122-
//! > so we will see struct_boxed_deconstruct.
123-
124121
//! > sierra_code
125122
label_test::foo::0:
126123
box_forward_snapshot<test::A>([0]) -> ([1])
127-
unbox<Snapshot<test::A>>([1]) -> ([2])
128-
store_temp<Snapshot<test::A>>([2]) -> ([2])
129-
struct_snapshot_deconstruct<test::A>([2]) -> ([3], [4])
130-
drop<Snapshot<Array<felt252>>>([4]) -> ()
131-
into_box<Snapshot<Array<felt252>>>([3]) -> ([5])
132-
return([5])
124+
dup<Box<Snapshot<test::A>>>([1]) -> ([1], [2])
125+
unbox<Snapshot<test::A>>([2]) -> ([3])
126+
drop<Snapshot<test::A>>([3]) -> ()
127+
struct_boxed_deconstruct<Snapshot<test::A>>([1]) -> ([4], [5])
128+
drop<Box<Snapshot<Array<felt252>>>>([5]) -> ()
129+
store_temp<Box<Snapshot<Array<felt252>>>>([4]) -> ([4])
130+
return([4])
133131

134132
//! > ==========================================================================
135133

@@ -157,14 +155,48 @@ struct A {
157155

158156
//! > lowering_diagnostics
159157

160-
//! > TODO(eytan-starkware): We want reboxing to apply to sierra in the future,
161-
//! > so we will see struct_boxed_deconstruct.
158+
//! > sierra_code
159+
label_test::foo::0:
160+
dup<Box<Snapshot<test::A>>>([0]) -> ([0], [1])
161+
unbox<Snapshot<test::A>>([1]) -> ([2])
162+
drop<Snapshot<test::A>>([2]) -> ()
163+
struct_boxed_deconstruct<Snapshot<test::A>>([0]) -> ([3], [4])
164+
drop<Box<Snapshot<Array<felt252>>>>([4]) -> ()
165+
store_temp<Box<Snapshot<Array<felt252>>>>([3]) -> ([3])
166+
return([3])
167+
168+
//! > ==========================================================================
169+
170+
//! > Test reboxing of a boxed, repeatedly snapshotted struct.
171+
172+
//! > test_runner_name
173+
test_function_generator(future_sierra:true)
174+
175+
//! > function_code
176+
fn foo(box: Box<@@@A>) -> Box<@@@Array<felt252>> {
177+
BoxTrait::new(box.unbox().a)
178+
}
179+
180+
//! > function_name
181+
foo
182+
183+
//! > module_code
184+
#[derive(Drop)]
185+
struct A {
186+
a: Array<felt252>,
187+
b: Array<felt252>,
188+
}
189+
190+
//! > semantic_diagnostics
191+
192+
//! > lowering_diagnostics
162193

163194
//! > sierra_code
164195
label_test::foo::0:
165-
unbox<Snapshot<test::A>>([0]) -> ([1])
166-
store_temp<Snapshot<test::A>>([1]) -> ([1])
167-
struct_snapshot_deconstruct<test::A>([1]) -> ([2], [3])
168-
drop<Snapshot<Array<felt252>>>([3]) -> ()
169-
into_box<Snapshot<Array<felt252>>>([2]) -> ([4])
170-
return([4])
196+
dup<Box<Snapshot<test::A>>>([0]) -> ([0], [1])
197+
unbox<Snapshot<test::A>>([1]) -> ([2])
198+
drop<Snapshot<test::A>>([2]) -> ()
199+
struct_boxed_deconstruct<Snapshot<test::A>>([0]) -> ([3], [4])
200+
drop<Box<Snapshot<Array<felt252>>>>([4]) -> ()
201+
store_temp<Box<Snapshot<Array<felt252>>>>([3]) -> ([3])
202+
return([3])

crates/cairo-lang-sierra-generator/src/utils.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ pub fn struct_deconstruct_libfunc_id(
112112
let is_snapshot = long_id.generic_id == SnapshotType::id();
113113
let is_box = long_id.generic_id == BoxType::id();
114114
Ok(if is_snapshot {
115-
let concrete_enum_type =
116-
extract_matches!(&long_id.generic_args[0], GenericArg::Type).clone();
117-
get_libfunc_id_with_generic_arg(db, "struct_snapshot_deconstruct", concrete_enum_type)
115+
let concrete_type = extract_matches!(&long_id.generic_args[0], GenericArg::Type).clone();
116+
get_libfunc_id_with_generic_arg(db, "struct_snapshot_deconstruct", concrete_type)
118117
} else if is_box {
119118
let inner_ty = extract_matches!(&long_id.generic_args[0], GenericArg::Type).clone();
120119
get_libfunc_id_with_generic_arg(db, "struct_boxed_deconstruct", inner_ty)

0 commit comments

Comments
 (0)