Skip to content

Commit ad3f92b

Browse files
(optimization): felt252_div constant propogation support
1 parent bc86fb3 commit ad3f92b

File tree

3 files changed

+171
-2
lines changed

3 files changed

+171
-2
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,37 @@ impl<'db, 'mt> ConstFoldingContext<'db, 'mt> {
550550
} else {
551551
None
552552
}
553+
} else if id == self.felt_div {
554+
// There is no need to check if divisor is 0 and throw error, because casting to NonZero
555+
// will add a panic that will always be thrown. TODO(eytan-starkware): Make
556+
// sure that NonZero on a const zero leads to a compile error.
557+
if let Some(rhs) = self.as_int(stmt.inputs[1].var_id)
558+
&& rhs.is_one()
559+
{
560+
// Check if dividing by 1 (returns the original value)
561+
self.var_info.insert(stmt.outputs[0], VarInfo::Var(stmt.inputs[0]));
562+
None
563+
} else if let Some(lhs) = self.as_int(stmt.inputs[0].var_id)
564+
&& lhs.is_zero()
565+
{
566+
// Check if 0 is being divided (returns 0)
567+
Some(self.propagate_zero_and_get_statement(stmt.outputs[0]))
568+
} else if let (Some(lhs), Some(rhs)) =
569+
(self.as_int(stmt.inputs[0].var_id), self.as_int(stmt.inputs[1].var_id))
570+
&& !rhs.is_zero()
571+
{
572+
// Constant fold when both operands are constants
573+
574+
// Use field_div for Felt252 division (requires non-zero divisor)
575+
let lhs_felt = Felt252::from(lhs);
576+
let rhs_felt = Felt252::from(rhs);
577+
// For non-zero divisor, use field_div; the libfunc should handle zero checks
578+
let rhs_nonzero = rhs_felt.try_into().expect("Non-zero divisor");
579+
let value = lhs_felt.field_div(&rhs_nonzero).to_bigint();
580+
Some(self.propagate_const_and_get_statement(value, stmt.outputs[0], false))
581+
} else {
582+
None
583+
}
553584
} else if self.wide_mul_fns.contains(&id) {
554585
let lhs = self.as_int_ex(stmt.inputs[0].var_id);
555586
let rhs = self.as_int(stmt.inputs[1].var_id);
@@ -1266,6 +1297,8 @@ pub struct ConstFoldingLibfuncInfo<'db> {
12661297
felt_add: ExternFunctionId<'db>,
12671298
/// The `felt252_mul` libfunc.
12681299
felt_mul: ExternFunctionId<'db>,
1300+
/// The `felt252_div` libfunc.
1301+
felt_div: ExternFunctionId<'db>,
12691302
/// The `into_box` libfunc.
12701303
into_box: ExternFunctionId<'db>,
12711304
/// The `unbox` libfunc.
@@ -1437,6 +1470,7 @@ impl<'db> ConstFoldingLibfuncInfo<'db> {
14371470
felt_sub: core.extern_function_id("felt252_sub"),
14381471
felt_add: core.extern_function_id("felt252_add"),
14391472
felt_mul: core.extern_function_id("felt252_mul"),
1473+
felt_div: core.extern_function_id("felt252_div"),
14401474
into_box: box_module.extern_function_id("into_box"),
14411475
unbox: box_module.extern_function_id("unbox"),
14421476
box_forward_snapshot: box_module.generic_function_id("box_forward_snapshot"),

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,16 @@ fn test_match_optimizer(
3333
.split();
3434
let function_id =
3535
ConcreteFunctionWithBodyId::from_semantic(db, test_function.concrete_function_id);
36-
37-
let mut before = db.lowered_body(function_id, LoweringStage::PreOptimizations).unwrap().clone();
36+
let mut before = db
37+
.lowered_body(function_id, LoweringStage::PreOptimizations)
38+
.unwrap_or_else(|_| {
39+
panic!(
40+
"Failed to get lowered body for function {:?}. Diagnostics: {:?}",
41+
function_id,
42+
db.module_lowering_diagnostics(test_function.module_id)
43+
)
44+
})
45+
.clone();
3846
OptimizationPhase::ApplyInlining { enable_const_folding: false }
3947
.apply(db, function_id, &mut before)
4048
.unwrap();

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

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6493,3 +6493,130 @@ End:
64936493
Return(v6)
64946494

64956495
//! > lowering_diagnostics
6496+
6497+
//! > ==========================================================================
6498+
6499+
//! > Felt252 div const fold.
6500+
6501+
//! > test_runner_name
6502+
test_match_optimizer
6503+
6504+
//! > function
6505+
fn foo() -> felt252 {
6506+
let x = core::felt252_div(6, 30);
6507+
x * 5
6508+
}
6509+
6510+
//! > function_name
6511+
foo
6512+
6513+
//! > module_code
6514+
use core::felt252;
6515+
6516+
//! > semantic_diagnostics
6517+
6518+
//! > before
6519+
Parameters:
6520+
blk0 (root):
6521+
Statements:
6522+
(v0: core::felt252) <- 6
6523+
(v1: core::zeroable::NonZero::<core::felt252>) <- NonZero(30)
6524+
(v2: core::felt252) <- core::felt252_div(v0, v1)
6525+
(v3: core::felt252) <- 5
6526+
(v4: core::felt252) <- core::felt252_mul(v2, v3)
6527+
End:
6528+
Return(v4)
6529+
6530+
//! > after
6531+
Parameters:
6532+
blk0 (root):
6533+
Statements:
6534+
(v0: core::felt252) <- 6
6535+
(v1: core::zeroable::NonZero::<core::felt252>) <- NonZero(30)
6536+
(v2: core::felt252) <- 2894802230932904970957858226476056084498485772265277359978473644908697616385
6537+
(v3: core::felt252) <- 5
6538+
(v4: core::felt252) <- 1
6539+
End:
6540+
Return(v4)
6541+
6542+
//! > lowering_diagnostics
6543+
6544+
//! > ==========================================================================
6545+
6546+
//! > Felt252 div by one.
6547+
6548+
//! > test_runner_name
6549+
test_match_optimizer
6550+
6551+
//! > function
6552+
fn foo(a: felt252) -> felt252 {
6553+
core::felt252_div(a, 1)
6554+
}
6555+
6556+
//! > function_name
6557+
foo
6558+
6559+
//! > module_code
6560+
use core::felt252;
6561+
6562+
//! > semantic_diagnostics
6563+
6564+
//! > before
6565+
Parameters: v0: core::felt252
6566+
blk0 (root):
6567+
Statements:
6568+
(v1: core::zeroable::NonZero::<core::felt252>) <- NonZero(1)
6569+
(v2: core::felt252) <- core::felt252_div(v0, v1)
6570+
End:
6571+
Return(v2)
6572+
6573+
//! > after
6574+
Parameters: v0: core::felt252
6575+
blk0 (root):
6576+
Statements:
6577+
(v1: core::zeroable::NonZero::<core::felt252>) <- NonZero(1)
6578+
(v2: core::felt252) <- core::felt252_div(v0, v1)
6579+
End:
6580+
Return(v0)
6581+
6582+
//! > lowering_diagnostics
6583+
6584+
//! > ==========================================================================
6585+
6586+
//! > Felt252 zero div.
6587+
6588+
//! > test_runner_name
6589+
test_match_optimizer
6590+
6591+
//! > function
6592+
fn foo(a: NonZero<felt252>) -> felt252 {
6593+
core::felt252_div(0, a)
6594+
}
6595+
6596+
//! > function_name
6597+
foo
6598+
6599+
//! > module_code
6600+
use core::felt252;
6601+
6602+
//! > semantic_diagnostics
6603+
6604+
//! > before
6605+
Parameters: v0: core::zeroable::NonZero::<core::felt252>
6606+
blk0 (root):
6607+
Statements:
6608+
(v1: core::felt252) <- 0
6609+
(v2: core::felt252) <- core::felt252_div(v1, v0)
6610+
End:
6611+
Return(v2)
6612+
6613+
//! > after
6614+
Parameters: v0: core::zeroable::NonZero::<core::felt252>
6615+
blk0 (root):
6616+
Statements:
6617+
(v1: core::felt252) <- 0
6618+
(v2: core::felt252) <- 0
6619+
End:
6620+
Return(v2)
6621+
6622+
//! > lowering_diagnostics

0 commit comments

Comments
 (0)