Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions corelib/src/integer.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -1102,10 +1102,12 @@ impl U256BitOr of BitOr<u256> {
}
}

const fn u256_from_felt252(lhs: felt252) -> u256 implicits(RangeCheck) nopanic {
match u128s_from_felt252(lhs) {
U128sFromFelt252Result::Narrow(low) => u256 { low, high: 0_u128 },
U128sFromFelt252Result::Wide((high, low)) => u256 { low, high },
#[feature("bounded-int-utils")]
fn u256_from_felt252(lhs: felt252) -> u256 implicits(RangeCheck) nopanic {
let (mut high, mut low) = core::internal::bounded_int::u128_guarantees_from_felt252(lhs);
u256 {
low: upcast(core::internal::bounded_int::guarantee_content(ref low)),
high: upcast(core::internal::bounded_int::guarantee_content(ref high)),
}
}

Expand Down
16 changes: 16 additions & 0 deletions corelib/src/internal/bounded_int.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ use crate::RangeCheck;
pub extern type BoundedInt<const MIN: felt252, const MAX: felt252>;
pub(crate) extern type BoundedIntGuarantee<const MIN: felt252, const MAX: felt252>;

impl BoundedIntGuaranteeDestruct<const MIN: felt252, const MAX: felt252> of Destruct<BoundedIntGuarantee<MIN, MAX>> {
fn destruct(self: BoundedIntGuarantee<MIN, MAX>) nopanic {
bounded_int_guarantee_verify(self);
}
}
extern fn bounded_int_guarantee_verify<const MIN: felt252, const MAX: felt252>(
value: BoundedIntGuarantee<MIN, MAX>,
) implicits(RangeCheck) nopanic;

impl BoundedIntCopy<const MIN: felt252, const MAX: felt252> of Copy<BoundedInt<MIN, MAX>>;
impl BoundedIntDrop<const MIN: felt252, const MAX: felt252> of Drop<BoundedInt<MIN, MAX>>;

Expand Down Expand Up @@ -259,6 +268,13 @@ extern fn bounded_int_guarantee_content<const MIN: felt252, const MAX: felt252>(
ref value: BoundedIntGuarantee<MIN, MAX>,
) -> BoundedInt<MIN, MAX> implicits() nopanic;

pub extern fn u128_guarantees_from_felt252(
value: felt252,
) -> (
BoundedIntGuarantee<0, 0xffffffffffffffffffffffffffffffff>,
BoundedIntGuarantee<0, 0xffffffffffffffffffffffffffffffff>,
) implicits(RangeCheck) nopanic;

/// Returns the negation of the given `felt252` value.
trait NegFelt252<const NUM: felt252> {
/// The negation of the given `felt252` value.
Expand Down
24 changes: 23 additions & 1 deletion crates/cairo-lang-semantic/src/items/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ impl<'a, 'r, 'mt> ConstantEvaluateContext<'a, 'r, 'mt> {

/// Returns true if the given function is allowed to be called in constant context.
fn is_function_const(&self, function_id: FunctionId<'a>) -> bool {
if function_id == self.panic_with_felt252 {
if function_id == self.panic_with_felt252 || function_id == self.u256_from_felt252 {
return true;
}
let db = self.db;
Expand Down Expand Up @@ -903,6 +903,25 @@ impl<'a, 'r, 'mt> ConstantEvaluateContext<'a, 'r, 'mt> {
SemanticDiagnosticKind::FailedConstantCalculation,
));
}
if expr.function == self.u256_from_felt252 {
let [arg] = &args[..] else {
return to_missing(
self.diagnostics
.report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::ConstCycle),
);
};
let Some(value) = arg.long(db).to_int() else {
return to_missing(self.diagnostics.report(
expr.stable_ptr.untyped(),
SemanticDiagnosticKind::ContinueOnlyAllowedInsideALoop,
));
};
return ConstValueId::from_int(
db,
expr.ty,
&felt252_for_downcast(value, &BigInt::ZERO),
);
}
let concrete_function =
or_return!(self.substitute(expr.function.get_concrete(db)).map_err(to_missing));
if let Some(calc_result) =
Expand Down Expand Up @@ -1350,6 +1369,8 @@ pub struct ConstCalcInfo<'db> {
false_const: ConstValueId<'db>,
/// The function for panicking with a felt252.
panic_with_felt252: FunctionId<'db>,
/// The function for converting a felt252 to u256.
u256_from_felt252: FunctionId<'db>,
/// The integer `upcast` style functions.
pub upcast_fns: UnorderedHashSet<ExternFunctionId<'db>>,
/// The integer `downcast` style functions, mapping to whether it returns a reversed Option
Expand Down Expand Up @@ -1408,6 +1429,7 @@ impl<'db> ConstCalcInfo<'db> {
false_const: ConstValue::Enum(false_variant(db), unit_const).intern(db),
unit_const,
panic_with_felt252: core.function_id("panic_with_felt252", vec![]),
u256_from_felt252: integer.function_id("u256_from_felt252", vec![]),
upcast_fns: FromIterator::from_iter([
bounded_int.extern_function_id("upcast"),
integer.extern_function_id("u8_to_felt252"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
BoundedIntConcreteLibfunc::U128ToU32Guarantees(_) => {
vec![ApChange::Known(9)]
}
BoundedIntConcreteLibfunc::U128GuaranteesFromFelt252(_) => {
vec![ApChange::Known(5)]
}
},
Circuit(CircuitConcreteLibfunc::TryIntoCircuitModulus(_)) => {
vec![ApChange::Known(1), ApChange::Known(1)]
Expand Down
3 changes: 3 additions & 0 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,9 @@ pub fn core_libfunc_cost(
BoundedIntConcreteLibfunc::U128ToU32Guarantees(_) => {
vec![ConstCost::steps(7).into()]
}
BoundedIntConcreteLibfunc::U128GuaranteesFromFelt252(_) => {
vec![ConstCost { steps: 7, holes: 0, range_checks: 1, range_checks96: 0 }.into()]
}
},
Circuit(libfunc) => match libfunc {
CircuitConcreteLibfunc::AddInput(_) => {
Expand Down
57 changes: 57 additions & 0 deletions crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ pub fn build(
build_guarantee_split(builder, &libfunc.divisor)
}
BoundedIntConcreteLibfunc::U128ToU32Guarantees(_) => build_u128_to_u32_guarantees(builder),
BoundedIntConcreteLibfunc::U128GuaranteesFromFelt252(_) => {
build_u128_guarantees_from_felt252(builder)
}
}
}

Expand Down Expand Up @@ -364,3 +367,57 @@ fn build_u128_to_u32_guarantees(
Default::default(),
))
}

fn build_u128_guarantees_from_felt252(
builder: CompiledInvocationBuilder<'_>,
) -> Result<CompiledInvocation, InvocationError> {
let [range_check_expression, expr_value] = builder.try_get_refs()?;
let range_check = range_check_expression.try_unpack_single()?;
let value = expr_value.try_unpack_single()?;

let u128_bound = u128_bound(); // = 2**128.
// Represent the maximal possible value (PRIME - 1) as 2**128 * max_x + max_y.
let max_x: i128 = 10633823966279327296825105735305134080;
let max_y: i128 = 0;
let mut casm_builder = CasmBuilder::with_capacity(14, 4);
add_input_variables! {casm_builder,
buffer(0) range_check;
deref value;
};
casm_build_extend! {casm_builder,
let orig_range_check = range_check;
tempvar x;
tempvar y;
const u128_limit = u128_bound.clone();
// Write value as 2**128 * x + y.
hint DivMod { lhs: value, rhs: u128_limit } into { quotient: x, remainder: y };
// Check that value = 2**128 * x + y (mod PRIME).
tempvar x_shifted = x * u128_limit;
assert value = x_shifted + y;
}
// Check that there is no overflow in the computation of 2**128 * x + y.
// Check that x <= max_x.
validate_lt(&mut casm_builder, range_check, x, &(max_x + 1).into());
casm_build_extend! {casm_builder,
// If x == max_x, check that y <= max_y == 0.
const max_y = max_y;
const minus_max_x = -max_x;
tempvar x_minus_max_x = x + minus_max_x;
jump XNotMaxX if x_minus_max_x != 0;
assert y = max_y;
XNotMaxX:

};
Ok(builder.build_from_casm_builder(
casm_builder,
[("Fallthrough", &[&[range_check], &[x], &[y]], None)],
CostValidationInfo {
builtin_infos: vec![BuiltinInfo {
cost_token_ty: CostTokenType::RangeCheck,
start: orig_range_check,
end: range_check,
}],
extra_costs: None,
},
))
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ fn build_u128_from_felt252(
},
))
}

/// Handles instruction for reversing the bytes of a u128.
pub fn build_u128_byte_reverse(
builder: CompiledInvocationBuilder<'_>,
Expand Down
35 changes: 35 additions & 0 deletions crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use super::non_zero::{NonZeroType, nonzero_ty};
use super::range_check::RangeCheckType;
use super::utils::{Range, reinterpret_cast_signature};
use crate::define_libfunc_hierarchy;
use crate::extensions::felt252::Felt252Type;
use crate::extensions::lib_func::{
BranchSignature, DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature,
SierraApChange, SignatureOnlyGenericLibfunc, SignatureSpecializationContext,
Expand Down Expand Up @@ -93,6 +94,7 @@ define_libfunc_hierarchy! {
GuaranteeVerify(BoundedIntGuaranteeVerifyLibfunc),
GuaranteeSplit(BoundedIntGuaranteeSplitLibfunc),
U128ToU32Guarantees(U128ToU32GuaranteesLibfunc),
U128GuaranteesFromFelt252(U128GuaranteesFromFelt252Libfunc),
}, BoundedIntConcreteLibfunc
}

Expand Down Expand Up @@ -819,6 +821,39 @@ impl NoGenericArgsGenericLibfunc for U128ToU32GuaranteesLibfunc {
}
}

/// Libfunc for converting a felt252 into u128s, as the high and low limbs of the felt252.
#[derive(Default)]
pub struct U128GuaranteesFromFelt252Libfunc {}
impl NoGenericArgsGenericLibfunc for U128GuaranteesFromFelt252Libfunc {
const STR_ID: &'static str = "u128_guarantees_from_felt252";

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
) -> Result<LibfuncSignature, SpecializationError> {
let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?;
let u128_guarantee = bounded_int_guarantee_ty(context, BigInt::ZERO, u128::MAX.into())?;
Ok(LibfuncSignature::new_non_branch_ex(
vec![
ParamSignature::new(range_check_type.clone()).with_allow_add_const(),
ParamSignature::new(context.get_concrete_type(Felt252Type::id(), &[])?),
],
vec![
OutputVarInfo::new_builtin(range_check_type),
OutputVarInfo {
ty: u128_guarantee.clone(),
ref_info: OutputVarReferenceInfo::SimpleDerefs,
},
OutputVarInfo {
ty: u128_guarantee,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
},
],
SierraApChange::Known { new_vars_only: false },
))
}
}

/// Extracts min and max values from generic args.
fn extract_bounds(args: &[GenericArg]) -> Result<(&BigInt, &BigInt), SpecializationError> {
match args {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
"u128_const",
"u128_eq",
"u128_guarantee_mul",
"u128_guarantees_from_felt252",
"u128_is_zero",
"u128_mul_guarantee_verify",
"u128_overflowing_add",
Expand Down
Loading
Loading