Skip to content

Commit ffde167

Browse files
(optimization): Reboxing now also works on whole var reboxing
1 parent ef335a5 commit ffde167

File tree

2 files changed

+84
-14
lines changed

2 files changed

+84
-14
lines changed

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

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use cairo_lang_semantic::types::{TypesSemantic, peel_snapshots, wrap_in_snapshot
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;
15+
use itertools::Itertools;
1516
use salsa::Database;
1617

1718
use crate::borrow_check::analysis::StatementLocation;
@@ -183,7 +184,10 @@ pub fn apply_reboxing_candidates<'db>(
183184

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

186-
for candidate in candidates {
187+
// We sort the candidates such that removal of statements can be done in reverse order.
188+
for candidate in
189+
candidates.iter().sorted_by_key(|candidate| candidate.into_box_location.1).rev()
190+
{
187191
apply_reboxing_candidate(db, lowered, candidate);
188192
}
189193
}
@@ -215,13 +219,29 @@ fn apply_reboxing_candidate<'db>(
215219
candidate.reboxed_var.index()
216220
);
217221

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;
222+
match &candidate.source {
223+
ReboxingValue::Revoked => (),
224+
ReboxingValue::Unboxed(id) => {
225+
replace_variable_usages(lowered, candidate.reboxed_var, *id);
226+
// Remove the statement that unboxes the struct.
227+
lowered.blocks[candidate.into_box_location.0]
228+
.statements
229+
.remove(candidate.into_box_location.1);
230+
}
231+
ReboxingValue::MemberOfUnboxed { source, member } => {
232+
replace_into_box_call(db, lowered, candidate, source, *member)
233+
}
224234
};
235+
}
236+
237+
/// Replaces the call to `into_box` with a call to `struct_boxed_deconstruct`.
238+
fn replace_into_box_call<'db>(
239+
db: &'db dyn Database,
240+
lowered: &mut Lowered<'db>,
241+
candidate: &ReboxCandidate,
242+
source: &Rc<ReboxingValue>,
243+
member: usize,
244+
) {
225245
let ReboxingValue::Unboxed(source_var) = **source else {
226246
// When source of the value is not `Unboxes`, it is a nested MemberOfUnboxed, which is not
227247
// supported yet.
@@ -232,7 +252,7 @@ fn apply_reboxing_candidate<'db>(
232252
db,
233253
&mut lowered.variables,
234254
source_var,
235-
*member,
255+
member,
236256
candidate.reboxed_var,
237257
&lowered.blocks[candidate.into_box_location.0].statements[candidate.into_box_location.1],
238258
) {
@@ -244,6 +264,59 @@ fn apply_reboxing_candidate<'db>(
244264
}
245265
}
246266

267+
/// Replaces all usages of old_var with new_var throughout the lowered function.
268+
fn replace_variable_usages(lowered: &mut Lowered<'_>, old_var: VariableId, new_var: VariableId) {
269+
if old_var == new_var {
270+
return;
271+
}
272+
273+
for block in lowered.blocks.iter_mut() {
274+
for stmt in &mut block.statements {
275+
for input in stmt.inputs_mut() {
276+
if input.var_id == old_var {
277+
input.var_id = new_var;
278+
}
279+
}
280+
}
281+
282+
match &mut block.end {
283+
BlockEnd::Return(var_usages, _) => {
284+
for return_var in var_usages.iter_mut() {
285+
if return_var.var_id == old_var {
286+
return_var.var_id = new_var;
287+
}
288+
}
289+
}
290+
BlockEnd::Goto(_, remapping) => {
291+
// Collect all changes needed
292+
let mut changes = Vec::new();
293+
for (dst, src_usage) in remapping.iter() {
294+
if *dst == old_var {
295+
changes.push((*dst, Some(new_var), *src_usage));
296+
} else if src_usage.var_id == old_var {
297+
let mut new_usage = *src_usage;
298+
new_usage.var_id = new_var;
299+
changes.push((*dst, None, new_usage));
300+
}
301+
}
302+
303+
// Apply changes
304+
for (old_key, new_key_opt, new_usage) in changes {
305+
if let Some(new_key) = new_key_opt {
306+
// Key needs to change
307+
remapping.swap_remove(&old_key);
308+
remapping.insert(new_key, new_usage);
309+
} else {
310+
// Only value needs to change
311+
remapping.insert(old_key, new_usage);
312+
}
313+
}
314+
}
315+
_ => {}
316+
}
317+
}
318+
}
319+
247320
/// Creates a struct_boxed_deconstruct call statement.
248321
/// Returns None if the call cannot be created.
249322
fn create_struct_boxed_deconstruct_call<'db>(

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ blk0 (root):
9797
Statements:
9898
(v1: test::Point) <- core::box::unbox::<test::Point>(v0)
9999
(v2: core::integer::u32, v3: core::integer::u32, v4: core::integer::u32) <- struct_destructure(v1)
100-
(v5: core::box::Box::<core::integer::u32>, v12: core::box::Box::<core::integer::u32>, v13: core::box::Box::<core::integer::u32>) <- struct_destructure(v0)
100+
(v5: core::box::Box::<core::integer::u32>, v14: core::box::Box::<core::integer::u32>, v15: core::box::Box::<core::integer::u32>) <- struct_destructure(v0)
101101
(v6: test::Point) <- core::box::unbox::<test::Point>(v0)
102102
(v7: core::integer::u32, v8: core::integer::u32, v9: core::integer::u32) <- struct_destructure(v6)
103-
(v14: core::box::Box::<core::integer::u32>, v15: core::box::Box::<core::integer::u32>, v10: core::box::Box::<core::integer::u32>) <- struct_destructure(v0)
103+
(v12: core::box::Box::<core::integer::u32>, v13: core::box::Box::<core::integer::u32>, v10: core::box::Box::<core::integer::u32>) <- struct_destructure(v0)
104104
(v11: (core::box::Box::<core::integer::u32>, core::box::Box::<core::integer::u32>)) <- struct_construct(v5, v10)
105105
End:
106106
Return(v11)
@@ -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

0 commit comments

Comments
 (0)