Skip to content

Commit e35621c

Browse files
committed
feature(sierra): Added a split fn from u128 to u32 guarantees.
SIERRA_UPDATE_MINOR_CHANGE_TAG=New libfunc.
1 parent 3814e9a commit e35621c

6 files changed

Lines changed: 153 additions & 2 deletions

File tree

crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,9 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
438438
+ if libfunc.range.upper.is_zero() { 0 } else { 1 };
439439
vec![ApChange::Known(ap_change)]
440440
}
441+
BoundedIntConcreteLibfunc::U128ToU32Guarantees(_) => {
442+
vec![ApChange::Known(9)]
443+
}
441444
},
442445
Circuit(CircuitConcreteLibfunc::TryIntoCircuitModulus(_)) => {
443446
vec![ApChange::Known(1), ApChange::Known(1)]

crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,9 @@ pub fn core_libfunc_cost(
560560
+ if libfunc.range.upper.is_zero() { 0 } else { 1 };
561561
vec![ConstCost { steps, holes: 0, range_checks: 2, range_checks96: 0 }.into()]
562562
}
563+
BoundedIntConcreteLibfunc::U128ToU32Guarantees(_) => {
564+
vec![ConstCost::steps(7).into()]
565+
}
563566
},
564567
Circuit(libfunc) => match libfunc {
565568
CircuitConcreteLibfunc::AddInput(_) => {

crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub fn build(
4949
BoundedIntConcreteLibfunc::GuaranteeVerify(libfunc) => {
5050
build_guarantee_verify(builder, libfunc)
5151
}
52+
BoundedIntConcreteLibfunc::U128ToU32Guarantees(_) => build_u128_to_u32_guarantees(builder),
5253
}
5354
}
5455

@@ -280,3 +281,48 @@ fn build_guarantee_verify(
280281
},
281282
))
282283
}
284+
285+
/// Build u128 split into 4 u32 guarantees.
286+
/// Splits a u128 into 4 u32 values using iterative DivMod hints.
287+
fn build_u128_to_u32_guarantees(
288+
builder: CompiledInvocationBuilder<'_>,
289+
) -> Result<CompiledInvocation, InvocationError> {
290+
let [value] = builder.try_get_single_cells()?;
291+
292+
let mut casm_builder = CasmBuilder::with_capacity(20, 12);
293+
add_input_variables!(casm_builder, deref value;);
294+
295+
let pow2_32: BigInt = BigInt::one().shl(32);
296+
297+
casm_build_extend! {casm_builder,
298+
const pow2_32 = pow2_32;
299+
tempvar w1_w2_w3_shifted;
300+
tempvar w2_w3_shifted;
301+
tempvar w3_shifted;
302+
tempvar w1_w2_w3;
303+
tempvar w2_w3;
304+
tempvar w0;
305+
tempvar w1;
306+
tempvar w2;
307+
tempvar w3;
308+
309+
hint DivMod { lhs: value, rhs: pow2_32 } into { quotient: w1_w2_w3, remainder: w0 };
310+
hint DivMod { lhs: w1_w2_w3, rhs: pow2_32 } into { quotient: w2_w3, remainder: w1 };
311+
hint DivMod { lhs: w2_w3, rhs: pow2_32 } into { quotient: w3, remainder: w2 };
312+
ap += 3;
313+
314+
// Verify reconstruction using Horner's method.
315+
assert w1_w2_w3_shifted = w1_w2_w3 * pow2_32;
316+
assert value = w1_w2_w3_shifted + w0;
317+
assert w2_w3_shifted = w2_w3 * pow2_32;
318+
assert w1_w2_w3 = w2_w3_shifted + w1;
319+
assert w3_shifted = w3 * pow2_32;
320+
assert w2_w3 = w3_shifted + w2;
321+
};
322+
323+
Ok(builder.build_from_casm_builder(
324+
casm_builder,
325+
[("Fallthrough", &[&[w0], &[w1], &[w2], &[w3]], None)],
326+
Default::default(),
327+
))
328+
}

crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use num_bigint::{BigInt, ToBigInt};
66
use num_traits::{One, Signed, ToPrimitive, Zero};
77
use starknet_types_core::felt::Felt as Felt252;
88

9+
use super::int::unsigned128::Uint128Type;
910
use super::non_zero::{NonZeroType, nonzero_ty};
1011
use super::range_check::RangeCheckType;
1112
use super::utils::{Range, reinterpret_cast_signature};
@@ -18,8 +19,8 @@ use crate::extensions::lib_func::{
1819
use crate::extensions::type_specialization_context::TypeSpecializationContext;
1920
use crate::extensions::types::TypeInfo;
2021
use crate::extensions::{
21-
ConcreteType, NamedLibfunc, NamedType, OutputVarReferenceInfo, SignatureBasedConcreteLibfunc,
22-
SpecializationError, args_as_single_type, args_as_two_types,
22+
ConcreteType, NamedLibfunc, NamedType, NoGenericArgsGenericLibfunc, OutputVarReferenceInfo,
23+
SignatureBasedConcreteLibfunc, SpecializationError, args_as_single_type, args_as_two_types,
2324
};
2425
use crate::ids::{ConcreteTypeId, GenericTypeId};
2526
use crate::program::GenericArg;
@@ -88,6 +89,7 @@ define_libfunc_hierarchy! {
8889
IsZero(BoundedIntIsZeroLibfunc),
8990
WrapNonZero(BoundedIntWrapNonZeroLibfunc),
9091
GuaranteeVerify(BoundedIntGuaranteeVerifyLibfunc),
92+
U128ToU32Guarantees(U128ToU32GuaranteesLibfunc),
9193
}, BoundedIntConcreteLibfunc
9294
}
9395

@@ -627,6 +629,46 @@ pub fn bounded_int_guarantee_ty(
627629
)
628630
}
629631

632+
/// Libfunc for splitting a u128 into 4 u32 guarantees.
633+
/// Returns 4 BoundedIntGuarantee<0, 2^32-1> values (from low to high).
634+
#[derive(Default)]
635+
pub struct U128ToU32GuaranteesLibfunc {}
636+
impl NoGenericArgsGenericLibfunc for U128ToU32GuaranteesLibfunc {
637+
const STR_ID: &'static str = "u128_to_u32_guarantees";
638+
639+
fn specialize_signature(
640+
&self,
641+
context: &dyn SignatureSpecializationContext,
642+
) -> Result<LibfuncSignature, SpecializationError> {
643+
let u128_ty = context.get_concrete_type(Uint128Type::id(), &[])?;
644+
let u32_guarantee_ty =
645+
bounded_int_guarantee_ty(context, BigInt::ZERO, BigInt::from(u32::MAX))?;
646+
647+
Ok(LibfuncSignature::new_non_branch(
648+
vec![u128_ty],
649+
vec![
650+
OutputVarInfo {
651+
ty: u32_guarantee_ty.clone(),
652+
ref_info: OutputVarReferenceInfo::SimpleDerefs,
653+
},
654+
OutputVarInfo {
655+
ty: u32_guarantee_ty.clone(),
656+
ref_info: OutputVarReferenceInfo::SimpleDerefs,
657+
},
658+
OutputVarInfo {
659+
ty: u32_guarantee_ty.clone(),
660+
ref_info: OutputVarReferenceInfo::SimpleDerefs,
661+
},
662+
OutputVarInfo {
663+
ty: u32_guarantee_ty,
664+
ref_info: OutputVarReferenceInfo::SimpleDerefs,
665+
},
666+
],
667+
SierraApChange::Known { new_vars_only: false },
668+
))
669+
}
670+
}
671+
630672
/// Extracts min and max values from generic args.
631673
fn extract_bounds(args: &[GenericArg]) -> Result<(&BigInt, &BigInt), SpecializationError> {
632674
match args {

crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@
216216
"u128_safe_divmod",
217217
"u128_sqrt",
218218
"u128_to_felt252",
219+
"u128_to_u32_guarantees",
219220
"u128s_from_felt252",
220221
"u16_bitwise",
221222
"u16_const",

tests/e2e_test_data/libfuncs/bounded_int

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,3 +1130,59 @@ store_temp<RangeCheck>([2]) -> ([2]);
11301130
return([2]);
11311131

11321132
test::foo@F0([0]: RangeCheck, [1]: BoundedIntGuarantee<0, 4294967295>) -> (RangeCheck);
1133+
1134+
//! > ==========================================================================
1135+
1136+
//! > u128_to_u32_guarantees libfunc.
1137+
1138+
//! > test_runner_name
1139+
SmallE2ETestRunner
1140+
1141+
//! > cairo_code
1142+
extern type BoundedIntGuarantee<const MIN: felt252, const MAX: felt252>;
1143+
type U32Guarantee = BoundedIntGuarantee<0, 0xffffffff>;
1144+
1145+
extern fn u128_to_u32_guarantees(
1146+
value: u128,
1147+
) -> (U32Guarantee, U32Guarantee, U32Guarantee, U32Guarantee) nopanic;
1148+
1149+
fn foo(value: u128) -> (U32Guarantee, U32Guarantee, U32Guarantee, U32Guarantee) {
1150+
u128_to_u32_guarantees(value)
1151+
}
1152+
1153+
//! > casm
1154+
%{ (memory[ap + 3], memory[ap + 5]) = divmod(memory[fp + -3], 4294967296) %}
1155+
%{ (memory[ap + 4], memory[ap + 6]) = divmod(memory[ap + 3], 4294967296) %}
1156+
%{ (memory[ap + 8], memory[ap + 7]) = divmod(memory[ap + 4], 4294967296) %}
1157+
ap += 3;
1158+
[ap + -3] = [ap + 0] * 4294967296, ap++;
1159+
[fp + -3] = [ap + -4] + [ap + 1], ap++;
1160+
[ap + -4] = [ap + -1] * 4294967296, ap++;
1161+
[ap + -3] = [ap + -5] + [ap + 0], ap++;
1162+
[ap + -5] = [ap + 1] * 4294967296, ap++;
1163+
[ap + -4] = [ap + -6] + [ap + -1], ap++;
1164+
[ap + 0] = [ap + -4], ap++;
1165+
[ap + 0] = [ap + -4], ap++;
1166+
[ap + 0] = [ap + -4], ap++;
1167+
[ap + 0] = [ap + -4], ap++;
1168+
ret;
1169+
1170+
//! > function_costs
1171+
test::foo: SmallOrderedMap({Const: 1100})
1172+
1173+
//! > sierra_code
1174+
type u128 = u128 [storable: true, drop: true, dup: true, zero_sized: false];
1175+
type BoundedIntGuarantee<0, 4294967295> = BoundedIntGuarantee<0, 4294967295> [storable: true, drop: false, dup: false, zero_sized: false];
1176+
type Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>> = Struct<ut@Tuple, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>> [storable: true, drop: false, dup: false, zero_sized: false];
1177+
1178+
libfunc u128_to_u32_guarantees = u128_to_u32_guarantees;
1179+
libfunc struct_construct<Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>>> = struct_construct<Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>>>;
1180+
libfunc store_temp<Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>>> = store_temp<Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>>>;
1181+
1182+
F0:
1183+
u128_to_u32_guarantees([0]) -> ([1], [2], [3], [4]);
1184+
struct_construct<Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>>>([1], [2], [3], [4]) -> ([5]);
1185+
store_temp<Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>>>([5]) -> ([5]);
1186+
return([5]);
1187+
1188+
test::foo@F0([0]: u128) -> (Tuple<BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>, BoundedIntGuarantee<0, 4294967295>>);

0 commit comments

Comments
 (0)