From 5601cdaa62e1b49b0e1c69d09c94d825631c6afd Mon Sep 17 00:00:00 2001 From: Ori Ziv Date: Thu, 22 Jun 2023 14:27:00 +0300 Subject: [PATCH] Added i*_diff and Ord. commit-id:fce91b1f --- corelib/src/integer.cairo | 101 ++++++++++++++++++ corelib/src/test/integer_test.cairo | 45 ++++++++ .../src/core_libfunc_ap_change.rs | 2 + .../src/core_libfunc_cost_base.rs | 8 ++ .../src/invocations/int/mod.rs | 89 ++++++++++++++- .../src/invocations/int/signed.rs | 5 +- .../src/invocations/int/signed128.rs | 5 +- .../src/invocations/int/unsigned.rs | 50 +-------- .../src/invocations/int/unsigned128.rs | 44 +------- .../src/extensions/modules/int/signed.rs | 73 ++++++++++++- .../src/extensions/modules/int/signed128.rs | 6 +- .../src/allowed_libfuncs_lists/all.json | 5 + .../test_data/erc20.casm.json | 37 +++---- .../new_syntax_test_contract.casm.json | 13 +-- .../test_data/token_bridge.casm.json | 26 ++--- tests/e2e_test_data/cmp | 8 +- tests/e2e_test_data/libfuncs/u128 | 2 +- tests/e2e_test_data/libfuncs/u16 | 2 +- tests/e2e_test_data/libfuncs/u32 | 2 +- tests/e2e_test_data/libfuncs/u64 | 2 +- tests/e2e_test_data/libfuncs/u8 | 2 +- tests/test_data/fib_array.casm | 4 +- tests/test_data/fib_u128.casm | 2 +- tests/test_data/fib_u128_checked.casm | 2 +- 24 files changed, 380 insertions(+), 155 deletions(-) diff --git a/corelib/src/integer.cairo b/corelib/src/integer.cairo index abaef1151a1..0e114a3c03a 100644 --- a/corelib/src/integer.cairo +++ b/corelib/src/integer.cairo @@ -1942,6 +1942,26 @@ impl I8MulEq of MulEq { } } +extern fn i8_diff(lhs: i8, rhs: i8) -> Result implicits(RangeCheck) nopanic; +impl I8PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i8, rhs: i8) -> bool { + i8_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i8, rhs: i8) -> bool { + i8_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i8, rhs: i8) -> bool { + i8_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i8, rhs: i8) -> bool { + i8_diff(rhs, lhs).into_is_err() + } +} + #[derive(Copy, Drop)] extern type i16; impl NumericLiterali16 of NumericLiteral; @@ -2013,6 +2033,26 @@ impl I16MulEq of MulEq { } } +extern fn i16_diff(lhs: i16, rhs: i16) -> Result implicits(RangeCheck) nopanic; +impl I16PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i16, rhs: i16) -> bool { + i16_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i16, rhs: i16) -> bool { + i16_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i16, rhs: i16) -> bool { + i16_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i16, rhs: i16) -> bool { + i16_diff(rhs, lhs).into_is_err() + } +} + #[derive(Copy, Drop)] extern type i32; impl NumericLiterali32 of NumericLiteral; @@ -2084,6 +2124,26 @@ impl I32MulEq of MulEq { } } +extern fn i32_diff(lhs: i32, rhs: i32) -> Result implicits(RangeCheck) nopanic; +impl I32PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i32, rhs: i32) -> bool { + i32_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i32, rhs: i32) -> bool { + i32_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i32, rhs: i32) -> bool { + i32_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i32, rhs: i32) -> bool { + i32_diff(rhs, lhs).into_is_err() + } +} + #[derive(Copy, Drop)] extern type i64; impl NumericLiterali64 of NumericLiteral; @@ -2155,6 +2215,26 @@ impl I64MulEq of MulEq { } } +extern fn i64_diff(lhs: i64, rhs: i64) -> Result implicits(RangeCheck) nopanic; +impl I64PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i64, rhs: i64) -> bool { + i64_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i64, rhs: i64) -> bool { + i64_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i64, rhs: i64) -> bool { + i64_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i64, rhs: i64) -> bool { + i64_diff(rhs, lhs).into_is_err() + } +} + #[derive(Copy, Drop)] extern type i128; impl NumericLiterali128 of NumericLiteral; @@ -2212,3 +2292,24 @@ impl I128SubEq of SubEq { self = Sub::sub(self, other); } } + + +extern fn i128_diff(lhs: i128, rhs: i128) -> Result implicits(RangeCheck) nopanic; +impl I128PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i128, rhs: i128) -> bool { + i128_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i128, rhs: i128) -> bool { + i128_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i128, rhs: i128) -> bool { + i128_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i128, rhs: i128) -> bool { + i128_diff(rhs, lhs).into_is_err() + } +} diff --git a/corelib/src/test/integer_test.cairo b/corelib/src/test/integer_test.cairo index 5ae2653363e..3cbbd1dca30 100644 --- a/corelib/src/test/integer_test.cairo +++ b/corelib/src/test/integer_test.cairo @@ -1147,6 +1147,15 @@ fn test_i8_operators() { assert_eq(@(2_i8 * -4_i8), @-8_i8, '2 * -4 == -8'); assert_eq(@(-1_i8 * -3_i8), @3_i8, '-1 * -3 == 3'); assert_eq(@(-2_i8 * -4_i8), @8_i8, '-2 * -4 == 8'); + assert_lt(1_i8, 4_i8, '1 < 4'); + assert_le(1_i8, 4_i8, '1 <= 4'); + assert(!(4_i8 < 4_i8), '!(4 < 4)'); + assert_le(5_i8, 5_i8, '5 <= 5'); + assert(!(5_i8 <= 4_i8), '!(5 <= 8)'); + assert_gt(5_i8, 2_i8, '5 > 2'); + assert_ge(5_i8, 2_i8, '5 >= 2'); + assert(!(3_i8 > 3_i8), '!(3 > 3)'); + assert_ge(3_i8, 3_i8, '3 >= 3'); } #[test] @@ -1229,6 +1238,15 @@ fn test_i16_operators() { assert_eq(@(2_i16 * -4_i16), @-8_i16, '2 * -4 == -8'); assert_eq(@(-1_i16 * -3_i16), @3_i16, '-1 * -3 == 3'); assert_eq(@(-2_i16 * -4_i16), @8_i16, '-2 * -4 == 8'); + assert_lt(1_i16, 4_i16, '1 < 4'); + assert_le(1_i16, 4_i16, '1 <= 4'); + assert(!(4_i16 < 4_i16), '!(4 < 4)'); + assert_le(5_i16, 5_i16, '5 <= 5'); + assert(!(5_i16 <= 4_i16), '!(5 <= 8)'); + assert_gt(5_i16, 2_i16, '5 > 2'); + assert_ge(5_i16, 2_i16, '5 >= 2'); + assert(!(3_i16 > 3_i16), '!(3 > 3)'); + assert_ge(3_i16, 3_i16, '3 >= 3'); } #[test] @@ -1311,6 +1329,15 @@ fn test_i32_operators() { assert_eq(@(2_i32 * -4_i32), @-8_i32, '2 * -4 == -8'); assert_eq(@(-1_i32 * -3_i32), @3_i32, '-1 * -3 == 3'); assert_eq(@(-2_i32 * -4_i32), @8_i32, '-2 * -4 == 8'); + assert_lt(1_i32, 4_i32, '1 < 4'); + assert_le(1_i32, 4_i32, '1 <= 4'); + assert(!(4_i32 < 4_i32), '!(4 < 4)'); + assert_le(5_i32, 5_i32, '5 <= 5'); + assert(!(5_i32 <= 4_i32), '!(5 <= 8)'); + assert_gt(5_i32, 2_i32, '5 > 2'); + assert_ge(5_i32, 2_i32, '5 >= 2'); + assert(!(3_i32 > 3_i32), '!(3 > 3)'); + assert_ge(3_i32, 3_i32, '3 >= 3'); } #[test] @@ -1401,6 +1428,15 @@ fn test_i64_operators() { assert_eq(@(2_i64 * -4_i64), @-8_i64, '2 * -4 == -8'); assert_eq(@(-1_i64 * -3_i64), @3_i64, '-1 * -3 == 3'); assert_eq(@(-2_i64 * -4_i64), @8_i64, '-2 * -4 == 8'); + assert_lt(1_i64, 4_i64, '1 < 4'); + assert_le(1_i64, 4_i64, '1 <= 4'); + assert(!(4_i64 < 4_i64), '!(4 < 4)'); + assert_le(5_i64, 5_i64, '5 <= 5'); + assert(!(5_i64 <= 4_i64), '!(5 <= 8)'); + assert_gt(5_i64, 2_i64, '5 > 2'); + assert_ge(5_i64, 2_i64, '5 >= 2'); + assert(!(3_i64 > 3_i64), '!(3 > 3)'); + assert_ge(3_i64, 3_i64, '3 >= 3'); } #[test] @@ -1483,6 +1519,15 @@ fn test_i128_operators() { assert_eq(@(-3_i128 + -6_i128), @-9_i128, '-3 + -6 == -9'); assert_eq(@(-3_i128 - -1_i128), @-2_i128, '-3 - -1 == -2'); assert_eq(@(-231_i128 - -131_i128), @-100_i128, '-231--131=-100'); + assert_lt(1_i128, 4_i128, '1 < 4'); + assert_le(1_i128, 4_i128, '1 <= 4'); + assert(!(4_i128 < 4_i128), '!(4 < 4)'); + assert_le(5_i128, 5_i128, '5 <= 5'); + assert(!(5_i128 <= 4_i128), '!(5 <= 8)'); + assert_gt(5_i128, 2_i128, '5 > 2'); + assert_ge(5_i128, 2_i128, '5 >= 2'); + assert(!(3_i128 > 3_i128), '!(3 > 3)'); + assert_ge(3_i128, 3_i128, '3 >= 3'); } #[test] diff --git a/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs b/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs index 6a50800dc5e..41b087d810f 100644 --- a/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs +++ b/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs @@ -189,6 +189,7 @@ pub fn core_libfunc_ap_change( Sint128Concrete::Operation(_) => { vec![ApChange::Known(3), ApChange::Known(4), ApChange::Known(4)] } + Sint128Concrete::Diff(_) => vec![ApChange::Known(2), ApChange::Known(3)], }, CoreConcreteLibfunc::Mem(libfunc) => match libfunc { MemConcreteLibfunc::StoreTemp(libfunc) => { @@ -325,5 +326,6 @@ fn sint_ap_change( SintConcrete::Operation(_) => { vec![ApChange::Known(4), ApChange::Known(4), ApChange::Known(4)] } + SintConcrete::Diff(_) => vec![ApChange::Known(2), ApChange::Known(3)], } } diff --git a/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs b/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs index 105e25dd23d..8d6ed30f283 100644 --- a/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs +++ b/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs @@ -629,6 +629,10 @@ fn sint_libfunc_cost( ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), ], + SintConcrete::Diff(_) => vec![ + (ConstCost { steps: 3, holes: 0, range_checks: 1 }).into(), + (ConstCost { steps: 5, holes: 0, range_checks: 1 }).into(), + ], } } @@ -656,6 +660,10 @@ fn s128_libfunc_cost(libfunc: &Sint128Concrete) -> Vec { ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), ], + Sint128Concrete::Diff(_) => vec![ + ConstCost { steps: 3, holes: 0, range_checks: 1 }.into(), + ConstCost { steps: 5, holes: 0, range_checks: 1 }.into(), + ], } } diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/int/mod.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/int/mod.rs index 2c9a1643b76..1adc4bd3ad4 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/int/mod.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/int/mod.rs @@ -2,9 +2,12 @@ use cairo_lang_casm::builder::CasmBuilder; use cairo_lang_casm::casm_build_extend; use cairo_lang_casm::cell_expression::CellExpression; use cairo_lang_sierra::extensions::int::{IntConstConcreteLibfunc, IntTraits}; +use num_bigint::BigInt; use super::{CompiledInvocation, CompiledInvocationBuilder, InvocationError}; -use crate::invocations::{add_input_variables, CostValidationInfo}; +use crate::invocations::{ + add_input_variables, get_non_fallthrough_statement_id, CostValidationInfo, +}; use crate::references::ReferenceExpression; pub mod signed; @@ -45,3 +48,87 @@ pub fn build_small_wide_mul( CostValidationInfo::default(), )) } + +/// Handles a small integer diff operation. +/// absolute distance between the inputs must be smaller than `limit`. +fn build_small_diff( + builder: CompiledInvocationBuilder<'_>, + limit: BigInt, +) -> Result { + let failure_handle_statement_id = get_non_fallthrough_statement_id(&builder); + let [range_check, a, b] = builder.try_get_single_cells()?; + let mut casm_builder = CasmBuilder::default(); + add_input_variables! {casm_builder, + buffer(0) range_check; + deref a; + deref b; + }; + casm_build_extend! {casm_builder, + let orig_range_check = range_check; + tempvar a_ge_b; + tempvar a_minus_b = a - b; + const u128_limit = (BigInt::from(u128::MAX) + 1) as BigInt; + const limit = limit; + hint TestLessThan {lhs: a_minus_b, rhs: limit} into {dst: a_ge_b}; + jump NoOverflow if a_ge_b != 0; + // Overflow (negative): + // Here we know that 0 - (limit - 1) <= a - b < 0. + tempvar fixed_a_minus_b = a_minus_b + u128_limit; + assert fixed_a_minus_b = *(range_check++); + let wrapping_a_minus_b = a_minus_b + limit; + jump Target; + NoOverflow: + assert a_minus_b = *(range_check++); + }; + Ok(builder.build_from_casm_builder( + casm_builder, + [ + ("Fallthrough", &[&[range_check], &[a_minus_b]], None), + ("Target", &[&[range_check], &[wrapping_a_minus_b]], Some(failure_handle_statement_id)), + ], + CostValidationInfo { + range_check_info: Some((orig_range_check, range_check)), + extra_costs: None, + }, + )) +} + +/// Handles a 128 bit diff operation. +fn build_128bit_diff( + builder: CompiledInvocationBuilder<'_>, +) -> Result { + let failure_handle_statement_id = get_non_fallthrough_statement_id(&builder); + let [range_check, a, b] = builder.try_get_single_cells()?; + let mut casm_builder = CasmBuilder::default(); + add_input_variables! {casm_builder, + buffer(0) range_check; + deref a; + deref b; + }; + casm_build_extend! {casm_builder, + let orig_range_check = range_check; + tempvar a_ge_b; + tempvar a_minus_b = a - b; + const u128_limit = (BigInt::from(u128::MAX) + 1) as BigInt; + hint TestLessThan {lhs: a_minus_b, rhs: u128_limit} into {dst: a_ge_b}; + jump NoOverflow if a_ge_b != 0; + // Overflow (negative): + // Here we know that 0 - (2**128 - 1) <= a - b < 0. + tempvar wrapping_a_minus_b = a_minus_b + u128_limit; + assert wrapping_a_minus_b = *(range_check++); + jump Target; + NoOverflow: + assert a_minus_b = *(range_check++); + }; + Ok(builder.build_from_casm_builder( + casm_builder, + [ + ("Fallthrough", &[&[range_check], &[a_minus_b]], None), + ("Target", &[&[range_check], &[wrapping_a_minus_b]], Some(failure_handle_statement_id)), + ], + CostValidationInfo { + range_check_info: Some((orig_range_check, range_check)), + extra_costs: None, + }, + )) +} diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed.rs index 2c657c4ec23..655e8bd032e 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed.rs @@ -7,7 +7,7 @@ use cairo_lang_sierra::extensions::is_zero::IsZeroTraits; use cairo_lang_sierra::program::{BranchInfo, BranchTarget}; use num_bigint::{BigInt, ToBigInt}; -use super::{add_input_variables, build_const, build_small_wide_mul}; +use super::{add_input_variables, build_const, build_small_diff, build_small_wide_mul}; use crate::invocations::misc::validate_under_limit; use crate::invocations::{ get_non_fallthrough_statement_id, misc, CompiledInvocation, CompiledInvocationBuilder, @@ -185,5 +185,8 @@ pub fn build_sint< SintConcrete::Operation(libfunc) => { build_sint_overflowing_operation(builder, LOWER_LIMIT, UPPER_LIMIT, libfunc.operator) } + SintConcrete::Diff(_) => { + build_small_diff(builder, BigInt::from(UPPER_LIMIT) + 1 - BigInt::from(LOWER_LIMIT)) + } } } diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed128.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed128.rs index 1f7f71dc0e0..708c849ceb7 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed128.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/int/signed128.rs @@ -1,7 +1,9 @@ use cairo_lang_sierra::extensions::int::signed128::Sint128Concrete; use super::signed::{build_sint_from_felt252, build_sint_overflowing_operation}; -use super::{build_const, CompiledInvocation, CompiledInvocationBuilder, InvocationError}; +use super::{ + build_128bit_diff, build_const, CompiledInvocation, CompiledInvocationBuilder, InvocationError, +}; use crate::invocations::misc; /// Builds instructions for Sierra s128 operations. @@ -18,5 +20,6 @@ pub fn build( Sint128Concrete::Operation(libfunc) => { build_sint_overflowing_operation(builder, i128::MIN, i128::MAX, libfunc.operator) } + Sint128Concrete::Diff(_) => build_128bit_diff(builder), } } diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned.rs index ca2a6f0c944..4dca0859f4c 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned.rs @@ -8,7 +8,7 @@ use cairo_lang_sierra::extensions::int::{IntMulTraits, IntOperator}; use cairo_lang_sierra::extensions::is_zero::IsZeroTraits; use num_bigint::{BigInt, ToBigInt}; -use super::{build_const, build_small_wide_mul}; +use super::{build_const, build_small_diff, build_small_wide_mul}; use crate::invocations::misc::validate_under_limit; use crate::invocations::{ add_input_variables, bitwise, get_non_fallthrough_statement_id, misc, CompiledInvocation, @@ -66,50 +66,6 @@ fn build_small_uint_overflowing_add( )) } -/// Handles a small uint overflowing sub operation. -/// All parameters values are smaller than `limit`. -fn build_small_uint_overflowing_sub( - builder: CompiledInvocationBuilder<'_>, - limit: BigInt, -) -> Result { - let failure_handle_statement_id = get_non_fallthrough_statement_id(&builder); - let [range_check, a, b] = builder.try_get_single_cells()?; - let mut casm_builder = CasmBuilder::default(); - add_input_variables! {casm_builder, - buffer(0) range_check; - deref a; - deref b; - }; - casm_build_extend! {casm_builder, - let orig_range_check = range_check; - tempvar a_ge_b; - tempvar a_minus_b = a - b; - const u128_limit = (BigInt::from(u128::MAX) + 1) as BigInt; - const limit = limit; - hint TestLessThanOrEqual {lhs: b, rhs: a} into {dst: a_ge_b}; - jump NoOverflow if a_ge_b != 0; - // Overflow (negative): - // Here we know that 0 - (limit - 1) <= a - b < 0. - tempvar fixed_a_minus_b = a_minus_b + u128_limit; - assert fixed_a_minus_b = *(range_check++); - let wrapping_a_minus_b = a_minus_b + limit; - jump Target; - NoOverflow: - assert a_minus_b = *(range_check++); - }; - Ok(builder.build_from_casm_builder( - casm_builder, - [ - ("Fallthrough", &[&[range_check], &[a_minus_b]], None), - ("Target", &[&[range_check], &[wrapping_a_minus_b]], Some(failure_handle_statement_id)), - ], - CostValidationInfo { - range_check_info: Some((orig_range_check, range_check)), - extra_costs: None, - }, - )) -} - /// Handles a small uint conversion from felt252. fn build_small_uint_from_felt252( builder: CompiledInvocationBuilder<'_>, @@ -295,9 +251,7 @@ pub fn build_uint misc::build_cell_eq(builder), UintConcrete::Operation(libfunc) => match libfunc.operator { IntOperator::OverflowingAdd => build_small_uint_overflowing_add(builder, LIMIT), - IntOperator::OverflowingSub => { - build_small_uint_overflowing_sub(builder, BigInt::from(LIMIT)) - } + IntOperator::OverflowingSub => build_small_diff(builder, BigInt::from(LIMIT)), }, UintConcrete::ToFelt252(_) => misc::build_identity(builder), UintConcrete::FromFelt252(_) => build_small_uint_from_felt252::(builder), diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned128.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned128.rs index ecdca1c2565..393833dd6b7 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned128.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned128.rs @@ -5,7 +5,7 @@ use cairo_lang_sierra::extensions::int::IntOperator; use num_bigint::BigInt; use num_traits::{Num, One}; -use super::build_const; +use super::{build_128bit_diff, build_const}; use crate::invocations::{ add_input_variables, bitwise, get_non_fallthrough_statement_id, misc, CompiledInvocation, CompiledInvocationBuilder, CostValidationInfo, InvocationError, @@ -19,7 +19,7 @@ pub fn build( match libfunc { Uint128Concrete::Operation(libfunc) => match libfunc.operator { IntOperator::OverflowingAdd => build_u128_overflowing_add(builder), - IntOperator::OverflowingSub => build_u128_overflowing_sub(builder), + IntOperator::OverflowingSub => build_128bit_diff(builder), }, Uint128Concrete::Divmod(_) => build_u128_divmod(builder), Uint128Concrete::GuaranteeMul(_) => build_u128_guarantee_mul(builder), @@ -75,46 +75,6 @@ fn build_u128_overflowing_add( )) } -/// Handles a u128 overflowing sub operation. -fn build_u128_overflowing_sub( - builder: CompiledInvocationBuilder<'_>, -) -> Result { - let failure_handle_statement_id = get_non_fallthrough_statement_id(&builder); - let [range_check, a, b] = builder.try_get_single_cells()?; - let mut casm_builder = CasmBuilder::default(); - add_input_variables! {casm_builder, - buffer(0) range_check; - deref a; - deref b; - }; - casm_build_extend! {casm_builder, - let orig_range_check = range_check; - tempvar a_ge_b; - tempvar a_minus_b = a - b; - const u128_limit = (BigInt::from(u128::MAX) + 1) as BigInt; - hint TestLessThanOrEqual {lhs: b, rhs: a} into {dst: a_ge_b}; - jump NoOverflow if a_ge_b != 0; - // Overflow (negative): - // Here we know that 0 - (2**128 - 1) <= a - b < 0. - tempvar wrapping_a_minus_b = a_minus_b + u128_limit; - assert wrapping_a_minus_b = *(range_check++); - jump Target; - NoOverflow: - assert a_minus_b = *(range_check++); - }; - Ok(builder.build_from_casm_builder( - casm_builder, - [ - ("Fallthrough", &[&[range_check], &[a_minus_b]], None), - ("Target", &[&[range_check], &[wrapping_a_minus_b]], Some(failure_handle_statement_id)), - ], - CostValidationInfo { - range_check_info: Some((orig_range_check, range_check)), - extra_costs: None, - }, - )) -} - /// Handles a u128 divmod operation. fn build_u128_divmod( builder: CompiledInvocationBuilder<'_>, diff --git a/crates/cairo-lang-sierra/src/extensions/modules/int/signed.rs b/crates/cairo-lang-sierra/src/extensions/modules/int/signed.rs index 37e69fed014..efd98384529 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/int/signed.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/int/signed.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; use super::signed128::Sint128Type; +use super::unsigned::{Uint16Type, Uint32Type, Uint64Type, Uint8Type}; use super::{ IntConstLibfunc, IntEqualLibfunc, IntFromFelt252Libfunc, IntMulTraits, IntOperationConcreteLibfunc, IntOperator, IntToFelt252Libfunc, IntTraits, IntType, @@ -13,7 +14,10 @@ use crate::extensions::lib_func::{ SierraApChange, SignatureSpecializationContext, SpecializationContext, }; use crate::extensions::range_check::RangeCheckType; -use crate::extensions::{GenericLibfunc, NamedType, OutputVarReferenceInfo, SpecializationError}; +use crate::extensions::{ + GenericLibfunc, NamedType, NoGenericArgsGenericLibfunc, OutputVarReferenceInfo, + SpecializationError, +}; use crate::ids::{GenericLibfuncId, GenericTypeId}; use crate::program::GenericArg; @@ -23,6 +27,11 @@ pub trait SintTraits: IntTraits { const OVERFLOWING_ADD: &'static str; /// The generic libfunc id for subtraction. const OVERFLOWING_SUB: &'static str; + /// The generic libfunc id for difference of signed integers, logically equivalent to + /// substraction of unsigned integers. + const DIFF: &'static str; + /// The generic type id of the equivalent unsigned integer type. + const UNSIGNED_INT_TYPE: GenericTypeId; } define_libfunc_hierarchy! { @@ -32,6 +41,7 @@ define_libfunc_hierarchy! { ToFelt252(IntToFelt252Libfunc), FromFelt252(IntFromFelt252Libfunc), Operation(SintOperationLibfunc), + Diff(SintDiffLibfunc), IsZero(IsZeroLibfunc), WideMul(IntWideMulLibfunc), }, SintConcrete @@ -123,12 +133,67 @@ impl GenericLibfunc for SintOperationLibfunc { + _phantom: PhantomData, +} +impl NoGenericArgsGenericLibfunc for SintDiffLibfunc { + const STR_ID: &'static str = TSintTraits::DIFF; + + fn specialize_signature( + &self, + context: &dyn SignatureSpecializationContext, + ) -> Result { + let signed_ty = context.get_concrete_type(TSintTraits::GENERIC_TYPE_ID, &[])?; + let unsigned_ty = context.get_concrete_type(TSintTraits::UNSIGNED_INT_TYPE, &[])?; + let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?; + + let signed_ty_param = ParamSignature::new(signed_ty); + let rc_output_info = OutputVarInfo::new_builtin(range_check_type.clone(), 0); + let wrapping_result_ref_info = if TSintTraits::IS_SMALL { + OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic) + } else { + OutputVarReferenceInfo::NewTempVar { idx: 0 } + }; + Ok(LibfuncSignature { + param_signatures: vec![ + ParamSignature::new(range_check_type).with_allow_add_const(), + signed_ty_param.clone(), + signed_ty_param, + ], + branch_signatures: vec![ + BranchSignature { + vars: vec![ + rc_output_info.clone(), + OutputVarInfo { + ty: unsigned_ty.clone(), + ref_info: OutputVarReferenceInfo::NewTempVar { idx: 0 }, + }, + ], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + BranchSignature { + vars: vec![ + rc_output_info, + OutputVarInfo { ty: unsigned_ty, ref_info: wrapping_result_ref_info }, + ], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + ], + fallthrough: Some(0), + }) + } +} + #[derive(Default)] pub struct Sint8Traits; impl SintTraits for Sint8Traits { const OVERFLOWING_ADD: &'static str = "i8_overflowing_add_impl"; const OVERFLOWING_SUB: &'static str = "i8_overflowing_sub_impl"; + const DIFF: &'static str = "i8_diff"; + const UNSIGNED_INT_TYPE: GenericTypeId = ::ID; } impl IntTraits for Sint8Traits { @@ -162,6 +227,8 @@ pub struct Sint16Traits; impl SintTraits for Sint16Traits { const OVERFLOWING_ADD: &'static str = "i16_overflowing_add_impl"; const OVERFLOWING_SUB: &'static str = "i16_overflowing_sub_impl"; + const DIFF: &'static str = "i16_diff"; + const UNSIGNED_INT_TYPE: GenericTypeId = ::ID; } impl IntTraits for Sint16Traits { @@ -195,6 +262,8 @@ pub struct Sint32Traits; impl SintTraits for Sint32Traits { const OVERFLOWING_ADD: &'static str = "i32_overflowing_add_impl"; const OVERFLOWING_SUB: &'static str = "i32_overflowing_sub_impl"; + const DIFF: &'static str = "i32_diff"; + const UNSIGNED_INT_TYPE: GenericTypeId = ::ID; } impl IntTraits for Sint32Traits { @@ -228,6 +297,8 @@ pub struct Sint64Traits; impl SintTraits for Sint64Traits { const OVERFLOWING_ADD: &'static str = "i64_overflowing_add_impl"; const OVERFLOWING_SUB: &'static str = "i64_overflowing_sub_impl"; + const DIFF: &'static str = "i64_diff"; + const UNSIGNED_INT_TYPE: GenericTypeId = ::ID; } impl IntTraits for Sint64Traits { diff --git a/crates/cairo-lang-sierra/src/extensions/modules/int/signed128.rs b/crates/cairo-lang-sierra/src/extensions/modules/int/signed128.rs index be196d6af03..ba821fc07e3 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/int/signed128.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/int/signed128.rs @@ -1,4 +1,5 @@ -use super::signed::{SintOperationLibfunc, SintTraits}; +use super::signed::{SintDiffLibfunc, SintOperationLibfunc, SintTraits}; +use super::unsigned128::Uint128Type; use super::{ IntConstLibfunc, IntEqualLibfunc, IntFromFelt252Libfunc, IntToFelt252Libfunc, IntTraits, IntType, @@ -18,6 +19,7 @@ define_libfunc_hierarchy! { ToFelt252(IntToFelt252Libfunc), FromFelt252(IntFromFelt252Libfunc), Operation(SintOperationLibfunc), + Diff(SintDiffLibfunc), IsZero(IsZeroLibfunc), }, Sint128Concrete } @@ -28,6 +30,8 @@ pub struct Sint128Traits; impl SintTraits for Sint128Traits { const OVERFLOWING_ADD: &'static str = "i128_overflowing_add_impl"; const OVERFLOWING_SUB: &'static str = "i128_overflowing_sub_impl"; + const DIFF: &'static str = "i128_diff"; + const UNSIGNED_INT_TYPE: GenericTypeId = ::ID; } impl IntTraits for Sint128Traits { diff --git a/crates/cairo-lang-starknet/src/allowed_libfuncs_lists/all.json b/crates/cairo-lang-starknet/src/allowed_libfuncs_lists/all.json index 1159f2685cc..d6d7a86c9f6 100644 --- a/crates/cairo-lang-starknet/src/allowed_libfuncs_lists/all.json +++ b/crates/cairo-lang-starknet/src/allowed_libfuncs_lists/all.json @@ -68,6 +68,7 @@ "get_execution_info_syscall", "hades_permutation", "i128_const", + "i128_diff", "i128_eq", "i128_is_zero", "i128_overflowing_add_impl", @@ -75,6 +76,7 @@ "i128_to_felt252", "i128_try_from_felt252", "i16_const", + "i16_diff", "i16_eq", "i16_is_zero", "i16_overflowing_add_impl", @@ -83,6 +85,7 @@ "i16_try_from_felt252", "i16_wide_mul", "i32_const", + "i32_diff", "i32_eq", "i32_is_zero", "i32_overflowing_add_impl", @@ -91,6 +94,7 @@ "i32_try_from_felt252", "i32_wide_mul", "i64_const", + "i64_diff", "i64_eq", "i64_is_zero", "i64_overflowing_add_impl", @@ -99,6 +103,7 @@ "i64_try_from_felt252", "i64_wide_mul", "i8_const", + "i8_diff", "i8_eq", "i8_is_zero", "i8_overflowing_add_impl", diff --git a/crates/cairo-lang-starknet/test_data/erc20.casm.json b/crates/cairo-lang-starknet/test_data/erc20.casm.json index 7db7143b196..b5b9b9e9879 100644 --- a/crates/cairo-lang-starknet/test_data/erc20.casm.json +++ b/crates/cairo-lang-starknet/test_data/erc20.casm.json @@ -7154,18 +7154,15 @@ 4790, [ { - "TestLessThanOrEqual": { + "TestLessThan": { "lhs": { "Deref": { - "register": "FP", - "offset": -3 + "register": "AP", + "offset": 0 } }, "rhs": { - "Deref": { - "register": "FP", - "offset": -5 - } + "Immediate": "0x100000000000000000000000000000000" }, "dst": { "register": "AP", @@ -7179,18 +7176,15 @@ 4813, [ { - "TestLessThanOrEqual": { + "TestLessThan": { "lhs": { "Deref": { - "register": "FP", - "offset": -4 + "register": "AP", + "offset": 0 } }, "rhs": { - "Deref": { - "register": "FP", - "offset": -6 - } + "Immediate": "0x100000000000000000000000000000000" }, "dst": { "register": "AP", @@ -7204,18 +7198,15 @@ 4833, [ { - "TestLessThanOrEqual": { + "TestLessThan": { "lhs": { "Deref": { "register": "AP", - "offset": -2 + "offset": 0 } }, "rhs": { - "Deref": { - "register": "AP", - "offset": -7 - } + "Immediate": "0x100000000000000000000000000000000" }, "dst": { "register": "AP", @@ -8046,19 +8037,19 @@ [ 4790, [ - "memory[ap + -1] = memory[fp + -3] <= memory[fp + -5]" + "memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456" ] ], [ 4813, [ - "memory[ap + -1] = memory[fp + -4] <= memory[fp + -6]" + "memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456" ] ], [ 4833, [ - "memory[ap + -1] = memory[ap + -2] <= memory[ap + -7]" + "memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456" ] ] ], diff --git a/crates/cairo-lang-starknet/test_data/new_syntax_test_contract.casm.json b/crates/cairo-lang-starknet/test_data/new_syntax_test_contract.casm.json index 88b1ea4cada..bddbc89eed6 100644 --- a/crates/cairo-lang-starknet/test_data/new_syntax_test_contract.casm.json +++ b/crates/cairo-lang-starknet/test_data/new_syntax_test_contract.casm.json @@ -2560,18 +2560,15 @@ 1282, [ { - "TestLessThanOrEqual": { + "TestLessThan": { "lhs": { "Deref": { - "register": "FP", - "offset": -3 + "register": "AP", + "offset": 0 } }, "rhs": { - "Deref": { - "register": "FP", - "offset": -4 - } + "Immediate": "0x100000000000000000000000000000000" }, "dst": { "register": "AP", @@ -3037,7 +3034,7 @@ [ 1282, [ - "memory[ap + -1] = memory[fp + -3] <= memory[fp + -4]" + "memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456" ] ], [ diff --git a/crates/cairo-lang-starknet/test_data/token_bridge.casm.json b/crates/cairo-lang-starknet/test_data/token_bridge.casm.json index ad137d13fb1..776e85dd9fb 100644 --- a/crates/cairo-lang-starknet/test_data/token_bridge.casm.json +++ b/crates/cairo-lang-starknet/test_data/token_bridge.casm.json @@ -4531,18 +4531,15 @@ 2663, [ { - "TestLessThanOrEqual": { + "TestLessThan": { "lhs": { "Deref": { - "register": "FP", - "offset": -3 + "register": "AP", + "offset": 0 } }, "rhs": { - "Deref": { - "register": "FP", - "offset": -5 - } + "Immediate": "0x100000000000000000000000000000000" }, "dst": { "register": "AP", @@ -4556,18 +4553,15 @@ 2698, [ { - "TestLessThanOrEqual": { + "TestLessThan": { "lhs": { "Deref": { - "register": "FP", - "offset": -4 + "register": "AP", + "offset": 0 } }, "rhs": { - "Deref": { - "register": "FP", - "offset": -6 - } + "Immediate": "0x100000000000000000000000000000000" }, "dst": { "register": "AP", @@ -5275,13 +5269,13 @@ [ 2663, [ - "memory[ap + -1] = memory[fp + -3] <= memory[fp + -5]" + "memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456" ] ], [ 2698, [ - "memory[ap + -1] = memory[fp + -4] <= memory[fp + -6]" + "memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456" ] ], [ diff --git a/tests/e2e_test_data/cmp b/tests/e2e_test_data/cmp index 69aab82ac68..cd3035980af 100644 --- a/tests/e2e_test_data/cmp +++ b/tests/e2e_test_data/cmp @@ -10,7 +10,7 @@ fn foo(a: u128, b: u128) -> bool { //! > casm [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; @@ -79,7 +79,7 @@ fn foo(a: u32, b: u32) -> bool { //! > casm [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 4294967296 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; @@ -148,7 +148,7 @@ fn foo(a: u128, b: u128) -> bool { //! > casm [fp + -3] = [ap + 1] + [fp + -4], ap++; -%{ memory[ap + -1] = memory[fp + -4] <= memory[fp + -3] %} +%{ memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; @@ -217,7 +217,7 @@ fn foo(a: u32, b: u32) -> bool { //! > casm [fp + -3] = [ap + 1] + [fp + -4], ap++; -%{ memory[ap + -1] = memory[fp + -4] <= memory[fp + -3] %} +%{ memory[ap + -1] = memory[ap + 0] < 4294967296 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/e2e_test_data/libfuncs/u128 b/tests/e2e_test_data/libfuncs/u128 index 859b8de7a89..c1395d8878d 100644 --- a/tests/e2e_test_data/libfuncs/u128 +++ b/tests/e2e_test_data/libfuncs/u128 @@ -74,7 +74,7 @@ fn foo(a: u128, b: u128) -> Result:: { //! > casm [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/e2e_test_data/libfuncs/u16 b/tests/e2e_test_data/libfuncs/u16 index e59293a1d64..a5c5e3affeb 100644 --- a/tests/e2e_test_data/libfuncs/u16 +++ b/tests/e2e_test_data/libfuncs/u16 @@ -75,7 +75,7 @@ fn foo(a: u16, b: u16) -> Result:: { //! > casm [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 65536 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/e2e_test_data/libfuncs/u32 b/tests/e2e_test_data/libfuncs/u32 index 533ff4244e9..f73c7bf394a 100644 --- a/tests/e2e_test_data/libfuncs/u32 +++ b/tests/e2e_test_data/libfuncs/u32 @@ -75,7 +75,7 @@ fn foo(a: u32, b: u32) -> Result:: { //! > casm [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 4294967296 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/e2e_test_data/libfuncs/u64 b/tests/e2e_test_data/libfuncs/u64 index c5ce169ff4b..e500fc5c85a 100644 --- a/tests/e2e_test_data/libfuncs/u64 +++ b/tests/e2e_test_data/libfuncs/u64 @@ -75,7 +75,7 @@ fn foo(a: u64, b: u64) -> Result:: { //! > casm [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 18446744073709551616 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/e2e_test_data/libfuncs/u8 b/tests/e2e_test_data/libfuncs/u8 index fcf53091c46..f9da807e5e8 100644 --- a/tests/e2e_test_data/libfuncs/u8 +++ b/tests/e2e_test_data/libfuncs/u8 @@ -75,7 +75,7 @@ fn foo(a: u8, b: u8) -> Result:: { //! > casm [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 256 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/test_data/fib_array.casm b/tests/test_data/fib_array.casm index be7b7ab34c2..482e0b1db1a 100644 --- a/tests/test_data/fib_array.casm +++ b/tests/test_data/fib_array.casm @@ -51,7 +51,7 @@ ret; ret; [fp + -3] = [ap + 0] + [fp + -4], ap++; [ap + -1] = [ap + 1] + [fp + -5], ap++; -%{ memory[ap + -1] = memory[fp + -5] <= memory[ap + -2] %} +%{ memory[ap + -1] = memory[ap + 0] < 4294967296 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -6] + 0]; @@ -123,7 +123,7 @@ ret; [ap + 0] = [ap + -4], ap++; ret; [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 4294967296 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/test_data/fib_u128.casm b/tests/test_data/fib_u128.casm index 9a66285ef4b..b48beb0b7f4 100644 --- a/tests/test_data/fib_u128.casm +++ b/tests/test_data/fib_u128.casm @@ -71,7 +71,7 @@ ret; [ap + 0] = [ap + -4], ap++; ret; [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0]; diff --git a/tests/test_data/fib_u128_checked.casm b/tests/test_data/fib_u128_checked.casm index 2080b6f061a..f01dd6fdd0f 100644 --- a/tests/test_data/fib_u128_checked.casm +++ b/tests/test_data/fib_u128_checked.casm @@ -52,7 +52,7 @@ jmp rel 8; [ap + 0] = 0, ap++; ret; [fp + -4] = [ap + 1] + [fp + -3], ap++; -%{ memory[ap + -1] = memory[fp + -3] <= memory[fp + -4] %} +%{ memory[ap + -1] = memory[ap + 0] < 340282366920938463463374607431768211456 %} jmp rel 7 if [ap + -1] != 0, ap++; [ap + 0] = [ap + -1] + 340282366920938463463374607431768211456, ap++; [ap + -1] = [[fp + -5] + 0];