Skip to content

Commit 4370c99

Browse files
(optimization): Reboxing now also works on whole var reboxing
1 parent 630ce29 commit 4370c99

File tree

2 files changed

+134
-21
lines changed

2 files changed

+134
-21
lines changed

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

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
1414
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
1515
use salsa::Database;
1616

17+
use super::var_renamer::VarRenamer;
1718
use crate::borrow_check::analysis::StatementLocation;
19+
use crate::utils::RebuilderEx;
1820
use crate::{
1921
BlockEnd, Lowered, Statement, StatementStructDestructure, VarUsage, Variable, VariableArena,
2022
VariableId,
@@ -183,8 +185,32 @@ pub fn apply_reboxing_candidates<'db>(
183185

184186
trace!("Applying {} reboxing optimization(s).", candidates.len());
185187

188+
let mut renamer = VarRenamer::default();
189+
let mut stmts_to_remove = Vec::new();
190+
186191
for candidate in candidates {
187-
apply_reboxing_candidate(db, lowered, candidate);
192+
match &candidate.source {
193+
ReboxingValue::Revoked => {}
194+
ReboxingValue::Unboxed(id) => {
195+
renamer.renamed_vars.insert(candidate.reboxed_var, *id);
196+
stmts_to_remove.push(candidate.into_box_location);
197+
}
198+
ReboxingValue::MemberOfUnboxed { source, member } => {
199+
replace_into_box_call(db, lowered, candidate, source, *member);
200+
}
201+
}
202+
}
203+
204+
// We sort the candidates such that removal of statements can be done in reverse order.
205+
// Statements are expected to be in order due to analysis being forward, but we do not require
206+
// the ordered assumption.
207+
stmts_to_remove.sort_by_key(|(block_id, stmt_idx)| (block_id.0, *stmt_idx));
208+
for (block_id, stmt_idx) in stmts_to_remove.into_iter().rev() {
209+
lowered.blocks[block_id].statements.remove(stmt_idx);
210+
}
211+
212+
for block in lowered.blocks.iter_mut() {
213+
*block = renamer.rebuild_block(block);
188214
}
189215
}
190216

@@ -203,25 +229,14 @@ pub fn apply_reboxing<'db>(db: &'db dyn Database, lowered: &mut Lowered<'db>) {
203229
}
204230
}
205231

206-
/// Applies a single reboxing optimization for the given candidate.
207-
fn apply_reboxing_candidate<'db>(
232+
/// Replaces the call to `into_box` with a call to `struct_boxed_deconstruct`.
233+
fn replace_into_box_call<'db>(
208234
db: &'db dyn Database,
209235
lowered: &mut Lowered<'db>,
210236
candidate: &ReboxCandidate,
237+
source: &Rc<ReboxingValue>,
238+
member: usize,
211239
) {
212-
trace!(
213-
"Applying optimization: candidate={}, reboxed={}",
214-
candidate.source,
215-
candidate.reboxed_var.index()
216-
);
217-
218-
// TODO(eytan-starkware): Handle snapshot of box (e.g., @Box<T>).
219-
// Only support MemberOfUnboxed where source is Unboxed for now.
220-
let ReboxingValue::MemberOfUnboxed { source, member } = &candidate.source else {
221-
// If source is not member of unboxed, we are reboxing original value which is not supported
222-
// yet.
223-
return;
224-
};
225240
let ReboxingValue::Unboxed(source_var) = **source else {
226241
// When source of the value is not `Unboxes`, it is a nested MemberOfUnboxed, which is not
227242
// supported yet.
@@ -232,7 +247,7 @@ fn apply_reboxing_candidate<'db>(
232247
db,
233248
&mut lowered.variables,
234249
source_var,
235-
*member,
250+
member,
236251
candidate.reboxed_var,
237252
&lowered.blocks[candidate.into_box_location.0].statements[candidate.into_box_location.1],
238253
) {

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

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,6 @@ test_reboxing_analysis
229229
//! > function_name
230230
rebox_whole
231231

232-
//! > TODO(eytan-starkware): Add support for whole var reboxing
233-
234232
//! > module_code
235233
struct Simple {
236234
value: felt252,
@@ -262,9 +260,8 @@ Parameters: v0: core::box::Box::<test::Simple>
262260
blk0 (root):
263261
Statements:
264262
(v1: test::Simple) <- core::box::unbox::<test::Simple>(v0)
265-
(v2: core::box::Box::<test::Simple>) <- core::box::into_box::<test::Simple>(v1)
266263
End:
267-
Return(v2)
264+
Return(v0)
268265

269266
//! > ==========================================================================
270267

@@ -751,3 +748,104 @@ Statements:
751748
(v7: (core::box::Box::<@core::felt252>, @test::NonDrop)) <- struct_construct(v4, v6)
752749
End:
753750
Return(v7)
751+
752+
//! > ==========================================================================
753+
754+
//! > Test mixed reboxing types in different blocks
755+
756+
//! > test_runner_name
757+
test_reboxing_analysis
758+
759+
//! > function_name
760+
mixed_reboxing
761+
762+
//! > TODO(eytan-starkware): When removing demand for Copy, check no double
763+
//! > into_box remain
764+
765+
//! > module_code
766+
#[derive(Drop, Copy)]
767+
struct Point {
768+
x: felt252,
769+
y: felt252,
770+
}
771+
772+
//! > function_code
773+
fn mixed_reboxing(p: Box<Point>, flag: bool) -> Box<felt252> {
774+
if flag {
775+
let unboxed = p.unbox();
776+
BoxTrait::new(unboxed.x)
777+
} else {
778+
let unboxed = p.unbox();
779+
let boxed = BoxTrait::new(unboxed);
780+
let unboxed2 = boxed.unbox();
781+
BoxTrait::new(unboxed2.y)
782+
}
783+
}
784+
785+
//! > candidates
786+
v5, v9, v14
787+
788+
//! > before
789+
Parameters: v0: core::box::Box::<test::Point>, v1: core::bool
790+
blk0 (root):
791+
Statements:
792+
End:
793+
Match(match_enum(v1) {
794+
bool::False(v2) => blk1,
795+
bool::True(v3) => blk2,
796+
})
797+
798+
blk1:
799+
Statements:
800+
(v4: test::Point) <- core::box::unbox::<test::Point>(v0)
801+
(v5: core::box::Box::<test::Point>) <- core::box::into_box::<test::Point>(v4)
802+
(v6: test::Point) <- core::box::unbox::<test::Point>(v5)
803+
(v7: core::felt252, v8: core::felt252) <- struct_destructure(v6)
804+
(v9: core::box::Box::<core::felt252>) <- core::box::into_box::<core::felt252>(v8)
805+
End:
806+
Goto(blk3, {v9 -> v10})
807+
808+
blk2:
809+
Statements:
810+
(v11: test::Point) <- core::box::unbox::<test::Point>(v0)
811+
(v12: core::felt252, v13: core::felt252) <- struct_destructure(v11)
812+
(v14: core::box::Box::<core::felt252>) <- core::box::into_box::<core::felt252>(v12)
813+
End:
814+
Goto(blk3, {v14 -> v10})
815+
816+
blk3:
817+
Statements:
818+
End:
819+
Return(v10)
820+
821+
//! > after
822+
Parameters: v0: core::box::Box::<test::Point>, v1: core::bool
823+
blk0 (root):
824+
Statements:
825+
End:
826+
Match(match_enum(v1) {
827+
bool::False(v2) => blk1,
828+
bool::True(v3) => blk2,
829+
})
830+
831+
blk1:
832+
Statements:
833+
(v4: test::Point) <- core::box::unbox::<test::Point>(v0)
834+
(v6: test::Point) <- core::box::unbox::<test::Point>(v0)
835+
(v7: core::felt252, v8: core::felt252) <- struct_destructure(v6)
836+
(v15: core::box::Box::<core::felt252>, v9: core::box::Box::<core::felt252>) <- struct_destructure(v0)
837+
End:
838+
Goto(blk3, {v9 -> v10})
839+
840+
blk2:
841+
Statements:
842+
(v11: test::Point) <- core::box::unbox::<test::Point>(v0)
843+
(v12: core::felt252, v13: core::felt252) <- struct_destructure(v11)
844+
(v14: core::box::Box::<core::felt252>, v16: core::box::Box::<core::felt252>) <- struct_destructure(v0)
845+
End:
846+
Goto(blk3, {v14 -> v10})
847+
848+
blk3:
849+
Statements:
850+
End:
851+
Return(v10)

0 commit comments

Comments
 (0)