diff --git a/corelib/src/integer.cairo b/corelib/src/integer.cairo index 6d32d8aa145..5afb8360c24 100644 --- a/corelib/src/integer.cairo +++ b/corelib/src/integer.cairo @@ -1864,6 +1864,13 @@ impl U256Zeroable of Zeroable { } } +enum SignedIntegerResult { + InRange: T, + Underflow: T, + Overflow: T, +} +impl SignedIntegerResultDrop> of Drop>; + #[derive(Copy, Drop)] extern type i8; impl NumericLiterali8 of NumericLiteral; @@ -1885,6 +1892,43 @@ impl I8PartialEq of PartialEq { } } +extern fn i8_overflowing_add_impl( + lhs: i8, rhs: i8 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i8_overflowing_sub_impl( + lhs: i8, rhs: i8 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I8Add of Add { + fn add(lhs: i8, rhs: i8) -> i8 { + match i8_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i8_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i8_add Overflow'), + } + } +} +impl I8AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i8, other: i8) { + self = Add::add(self, other); + } +} +impl I8Sub of Sub { + fn sub(lhs: i8, rhs: i8) -> i8 { + match i8_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i8_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i8_sub Overflow'), + } + } +} +impl I8SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i8, other: i8) { + self = Sub::sub(self, other); + } +} + #[derive(Copy, Drop)] extern type i16; impl NumericLiterali16 of NumericLiteral; @@ -1906,6 +1950,43 @@ impl I16PartialEq of PartialEq { } } +extern fn i16_overflowing_add_impl( + lhs: i16, rhs: i16 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i16_overflowing_sub_impl( + lhs: i16, rhs: i16 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I16Add of Add { + fn add(lhs: i16, rhs: i16) -> i16 { + match i16_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i16_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i16_add Overflow'), + } + } +} +impl I16AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i16, other: i16) { + self = Add::add(self, other); + } +} +impl I16Sub of Sub { + fn sub(lhs: i16, rhs: i16) -> i16 { + match i16_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i16_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i16_sub Overflow'), + } + } +} +impl I16SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i16, other: i16) { + self = Sub::sub(self, other); + } +} + #[derive(Copy, Drop)] extern type i32; impl NumericLiterali32 of NumericLiteral; @@ -1927,6 +2008,43 @@ impl I32PartialEq of PartialEq { } } +extern fn i32_overflowing_add_impl( + lhs: i32, rhs: i32 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i32_overflowing_sub_impl( + lhs: i32, rhs: i32 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I32Add of Add { + fn add(lhs: i32, rhs: i32) -> i32 { + match i32_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i32_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i32_add Overflow'), + } + } +} +impl I32AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i32, other: i32) { + self = Add::add(self, other); + } +} +impl I32Sub of Sub { + fn sub(lhs: i32, rhs: i32) -> i32 { + match i32_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i32_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i32_sub Overflow'), + } + } +} +impl I32SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i32, other: i32) { + self = Sub::sub(self, other); + } +} + #[derive(Copy, Drop)] extern type i64; impl NumericLiterali64 of NumericLiteral; @@ -1948,6 +2066,43 @@ impl I64PartialEq of PartialEq { } } +extern fn i64_overflowing_add_impl( + lhs: i64, rhs: i64 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i64_overflowing_sub_impl( + lhs: i64, rhs: i64 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I64Add of Add { + fn add(lhs: i64, rhs: i64) -> i64 { + match i64_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i64_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i64_add Overflow'), + } + } +} +impl I64AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i64, other: i64) { + self = Add::add(self, other); + } +} +impl I64Sub of Sub { + fn sub(lhs: i64, rhs: i64) -> i64 { + match i64_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i64_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i64_sub Overflow'), + } + } +} +impl I64SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i64, other: i64) { + self = Sub::sub(self, other); + } +} + #[derive(Copy, Drop)] extern type i128; impl NumericLiterali128 of NumericLiteral; @@ -1968,3 +2123,40 @@ impl I128PartialEq of PartialEq { !(*lhs == *rhs) } } + +extern fn i128_overflowing_add_impl( + lhs: i128, rhs: i128 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i128_overflowing_sub_impl( + lhs: i128, rhs: i128 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I128Add of Add { + fn add(lhs: i128, rhs: i128) -> i128 { + match i128_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i128_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i128_add Overflow'), + } + } +} +impl I128AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i128, other: i128) { + self = Add::add(self, other); + } +} +impl I128Sub of Sub { + fn sub(lhs: i128, rhs: i128) -> i128 { + match i128_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i128_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i128_sub Overflow'), + } + } +} +impl I128SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i128, other: i128) { + self = Sub::sub(self, other); + } +} diff --git a/corelib/src/test/integer_test.cairo b/corelib/src/test/integer_test.cairo index 9de0cf454e8..5f275b8582b 100644 --- a/corelib/src/test/integer_test.cairo +++ b/corelib/src/test/integer_test.cairo @@ -1130,8 +1130,51 @@ fn test_i8_operators() { assert_eq(@-0x80_felt252.try_into().unwrap(), @-0x80_i8, '-0x80 is not i8'); let v: Option = -0x81_felt252.try_into(); assert(v.is_none(), '-0x81 is i8'); + assert_eq(@(1_i8 + 3_i8), @4_i8, '1 + 3 == 4'); + assert_eq(@(3_i8 + 6_i8), @9_i8, '3 + 6 == 9'); + assert_eq(@(3_i8 - 1_i8), @2_i8, '3 - 1 == 2'); + assert_eq(@(121_i8 - 21_i8), @100_i8, '121-21=100'); + assert_eq(@(-1_i8 + -3_i8), @-4_i8, '-1 + -3 == -4'); + assert_eq(@(-3_i8 + -6_i8), @-9_i8, '-3 + -6 == -9'); + assert_eq(@(-3_i8 - -1_i8), @-2_i8, '-3 - -1 == -2'); + assert_eq(@(-121_i8 - -21_i8), @-100_i8, '-121--21=-100'); } +#[test] +#[should_panic(expected: ('i8_sub Underflow', ))] +fn test_i8_sub_overflow_1() { + -0x80_i8 - 1_i8; +} + +#[test] +#[should_panic(expected: ('i8_sub Underflow', ))] +fn test_i8_sub_overflow_2() { + -0x80_i8 - 3_i8; +} + +#[test] +#[should_panic(expected: ('i8_sub Underflow', ))] +fn test_i8_sub_overflow_3() { + -0x7f_i8 - 3_i8; +} + +#[test] +#[should_panic(expected: ('i8_sub Underflow', ))] +fn test_i8_sub_overflow_4() { + -0x32_i8 - 0x7d_i8; +} + +#[test] +#[should_panic(expected: ('i8_add Overflow', ))] +fn test_i8_add_overflow_1() { + 0x40_i8 + 0x40_i8; +} + +#[test] +#[should_panic(expected: ('i8_add Overflow', ))] +fn test_i8_add_overflow_2() { + 0x64_i8 + 0x1e_i8; +} #[test] fn test_i16_operators() { @@ -1143,8 +1186,51 @@ fn test_i16_operators() { assert_eq(@-0x8000_felt252.try_into().unwrap(), @-0x8000_i16, '-0x8000 is not i16'); let v: Option = -0x8001_felt252.try_into(); assert(v.is_none(), '-0x8001 is i16'); + assert_eq(@(1_i16 + 3_i16), @4_i16, '1 + 3 == 4'); + assert_eq(@(3_i16 + 6_i16), @9_i16, '3 + 6 == 9'); + assert_eq(@(3_i16 - 1_i16), @2_i16, '3 - 1 == 2'); + assert_eq(@(231_i16 - 131_i16), @100_i16, '231-131=100'); + assert_eq(@(-1_i16 + -3_i16), @-4_i16, '-1 + -3 == -4'); + assert_eq(@(-3_i16 + -6_i16), @-9_i16, '-3 + -6 == -9'); + assert_eq(@(-3_i16 - -1_i16), @-2_i16, '-3 - -1 == -2'); + assert_eq(@(-231_i16 - -131_i16), @-100_i16, '-231--131=-100'); } +#[test] +#[should_panic(expected: ('i16_sub Underflow', ))] +fn test_i16_sub_overflow_1() { + -0x8000_i16 - 1_i16; +} + +#[test] +#[should_panic(expected: ('i16_sub Underflow', ))] +fn test_i16_sub_overflow_2() { + -0x8000_i16 - 3_i16; +} + +#[test] +#[should_panic(expected: ('i16_sub Underflow', ))] +fn test_i16_sub_overflow_3() { + -0x7fff_i16 - 3_i16; +} + +#[test] +#[should_panic(expected: ('i16_sub Underflow', ))] +fn test_i16_sub_overflow_4() { + -0x3200_i16 - 0x7d00_i16; +} + +#[test] +#[should_panic(expected: ('i16_add Overflow', ))] +fn test_i16_add_overflow_1() { + 0x4000_i16 + 0x4000_i16; +} + +#[test] +#[should_panic(expected: ('i16_add Overflow', ))] +fn test_i16_add_overflow_2() { + 0x6400_i16 + 0x1e00_i16; +} #[test] fn test_i32_operators() { @@ -1156,6 +1242,50 @@ fn test_i32_operators() { assert_eq(@-0x80000000_felt252.try_into().unwrap(), @-0x80000000_i32, '-0x8000 is not i32'); let v: Option = -0x80000001_felt252.try_into(); assert(v.is_none(), '-0x80000001 is i32'); + assert_eq(@(1_i32 + 3_i32), @4_i32, '1 + 3 == 4'); + assert_eq(@(3_i32 + 6_i32), @9_i32, '3 + 6 == 9'); + assert_eq(@(3_i32 - 1_i32), @2_i32, '3 - 1 == 2'); + assert_eq(@(231_i32 - 131_i32), @100_i32, '231-131=100'); + assert_eq(@(-1_i32 + -3_i32), @-4_i32, '-1 + -3 == -4'); + assert_eq(@(-3_i32 + -6_i32), @-9_i32, '-3 + -6 == -9'); + assert_eq(@(-3_i32 - -1_i32), @-2_i32, '-3 - -1 == -2'); + assert_eq(@(-231_i32 - -131_i32), @-100_i32, '-231--131=-100'); +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow', ))] +fn test_i32_sub_overflow_1() { + -0x80000000_i32 - 1_i32; +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow', ))] +fn test_i32_sub_overflow_2() { + -0x80000000_i32 - 3_i32; +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow', ))] +fn test_i32_sub_overflow_3() { + -0x7fffffff_i32 - 3_i32; +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow', ))] +fn test_i32_sub_overflow_4() { + -0x32000000_i32 - 0x7d000000_i32; +} + +#[test] +#[should_panic(expected: ('i32_add Overflow', ))] +fn test_i32_add_overflow_1() { + 0x40000000_i32 + 0x40000000_i32; +} + +#[test] +#[should_panic(expected: ('i32_add Overflow', ))] +fn test_i32_add_overflow_2() { + 0x64000000_i32 + 0x1e000000_i32; } @@ -1177,6 +1307,50 @@ fn test_i64_operators() { ); let v: Option = (-0x8000000000000001_felt252).try_into(); assert(v.is_none(), '-0x8000000000000001 is i64'); + assert_eq(@(1_i64 + 3_i64), @4_i64, '1 + 3 == 4'); + assert_eq(@(3_i64 + 6_i64), @9_i64, '3 + 6 == 9'); + assert_eq(@(3_i64 - 1_i64), @2_i64, '3 - 1 == 2'); + assert_eq(@(231_i64 - 131_i64), @100_i64, '231-131=100'); + assert_eq(@(-1_i64 + -3_i64), @-4_i64, '-1 + -3 == -4'); + assert_eq(@(-3_i64 + -6_i64), @-9_i64, '-3 + -6 == -9'); + assert_eq(@(-3_i64 - -1_i64), @-2_i64, '-3 - -1 == -2'); + assert_eq(@(-231_i64 - -131_i64), @-100_i64, '-231--131=-100'); +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow', ))] +fn test_i64_sub_overflow_1() { + -0x8000000000000000_i64 - 1_i64; +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow', ))] +fn test_i64_sub_overflow_2() { + -0x8000000000000000_i64 - 3_i64; +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow', ))] +fn test_i64_sub_overflow_3() { + -0x7fffffffffffffff_i64 - 3_i64; +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow', ))] +fn test_i64_sub_overflow_4() { + -0x3200000000000000_i64 - 0x7d00000000000000_i64; +} + +#[test] +#[should_panic(expected: ('i64_add Overflow', ))] +fn test_i64_add_overflow_1() { + 0x4000000000000000_i64 + 0x4000000000000000_i64; +} + +#[test] +#[should_panic(expected: ('i64_add Overflow', ))] +fn test_i64_add_overflow_2() { + 0x6400000000000000_i64 + 0x1e00000000000000_i64; } @@ -1198,4 +1372,48 @@ fn test_i128_operators() { ); let v: Option = (-0x80000000000000000000000000000001_felt252).try_into(); assert(v.is_none(), '-0x80..01 is i128'); + assert_eq(@(1_i128 + 3_i128), @4_i128, '1 + 3 == 4'); + 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_eq(@(-1_i128 + -3_i128), @-4_i128, '-1 + -3 == -4'); + 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'); +} + +#[test] +#[should_panic] +fn test_i128_sub_overflow_1() { + -0x80000000000000000000000000000000_i128 - 1_i128; +} + +#[test] +#[should_panic(expected: ('i128_sub Underflow', ))] +fn test_i128_sub_overflow_2() { + -0x80000000000000000000000000000000_i128 - 3_i128; +} + +#[test] +#[should_panic(expected: ('i128_sub Underflow', ))] +fn test_i128_sub_overflow_3() { + -0x7fffffffffffffffffffffffffffffff_i128 - 3_i128; +} + +#[test] +#[should_panic(expected: ('i128_sub Underflow', ))] +fn test_i128_sub_overflow_4() { + -0x32000000000000000000000000000000_i128 - 0x7d000000000000000000000000000000_i128; +} + +#[test] +#[should_panic(expected: ('i128_add Overflow', ))] +fn test_i128_add_overflow_1() { + 0x40000000000000000000000000000000_i128 + 0x40000000000000000000000000000000_i128; +} + +#[test] +#[should_panic(expected: ('i128_add Overflow', ))] +fn test_i128_add_overflow_2() { + 0x64000000000000000000000000000000_i128 + 0x1e000000000000000000000000000000_i128; } 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 62528a45f62..6a50800dc5e 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 @@ -186,6 +186,9 @@ pub fn core_libfunc_ap_change( vec![ApChange::Known(0)] } Sint128Concrete::IsZero(_) => vec![ApChange::Known(0), ApChange::Known(0)], + Sint128Concrete::Operation(_) => { + vec![ApChange::Known(3), ApChange::Known(4), ApChange::Known(4)] + } }, CoreConcreteLibfunc::Mem(libfunc) => match libfunc { MemConcreteLibfunc::StoreTemp(libfunc) => { @@ -319,5 +322,8 @@ fn sint_ap_change( SintConcrete::FromFelt252(_) => vec![ApChange::Known(3), ApChange::Known(7)], SintConcrete::IsZero(_) => vec![ApChange::Known(0), ApChange::Known(0)], SintConcrete::WideMul(_) => vec![ApChange::Known(0)], + SintConcrete::Operation(_) => { + vec![ApChange::Known(4), ApChange::Known(4), ApChange::Known(4)] + } } } 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 cf2d04dc892..105e25dd23d 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 @@ -624,6 +624,11 @@ fn sint_libfunc_cost( ] } SintConcrete::IsZero(_) => vec![ConstCost::steps(1).into(), ConstCost::steps(1).into()], + SintConcrete::Operation(_) => vec![ + ConstCost { steps: 6, holes: 0, range_checks: 2 }.into(), + ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), + ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), + ], } } @@ -646,6 +651,11 @@ fn s128_libfunc_cost(libfunc: &Sint128Concrete) -> Vec { Sint128Concrete::Equal(_) => { vec![steps(2).into(), steps(3).into()] } + Sint128Concrete::Operation(_) => vec![ + ConstCost { steps: 4, holes: 0, range_checks: 1 }.into(), + ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), + ConstCost { steps: 6, holes: 0, range_checks: 1 }.into(), + ], } } 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 3978a0b0e16..855e4440914 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 @@ -2,8 +2,9 @@ use cairo_felt::Felt252; use cairo_lang_casm::builder::CasmBuilder; use cairo_lang_casm::casm_build_extend; use cairo_lang_sierra::extensions::int::signed::{SintConcrete, SintTraits}; -use cairo_lang_sierra::extensions::int::IntMulTraits; +use cairo_lang_sierra::extensions::int::{IntMulTraits, IntOperator}; 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}; @@ -75,6 +76,99 @@ pub fn build_sint_from_felt252( )) } +/// Handles addition of signed integers. +/// `[min_value, max_value]` is the range of the signed integer. +pub fn build_sint_overflowing_operation( + builder: CompiledInvocationBuilder<'_>, + min_value: i128, + max_value: i128, + op: IntOperator, +) -> Result { + let [range_check, lhs, rhs] = builder.try_get_single_cells()?; + let [ + BranchInfo { target: BranchTarget::Fallthrough, results: _ }, + BranchInfo { target: BranchTarget::Statement(underflow_handle_statement_id), results: _ }, + BranchInfo { target: BranchTarget::Statement(overflow_handle_statement_id), results: _ }, + ] = builder.invocation.branches.as_slice() + else { + panic!("malformed invocation"); + }; + let mut casm_builder = CasmBuilder::default(); + add_input_variables! {casm_builder, + buffer(1) range_check; + deref lhs; + deref rhs; + }; + casm_build_extend! {casm_builder, + let orig_range_check = range_check; + tempvar value; + } + match op { + IntOperator::OverflowingAdd => { + casm_build_extend! {casm_builder, assert value = lhs + rhs;}; + } + IntOperator::OverflowingSub => { + casm_build_extend! {casm_builder, assert value = lhs - rhs;}; + } + } + casm_build_extend! {casm_builder, + const positive_range_fixer = -BigInt::from(min_value); + const range_size = (BigInt::from(max_value) - BigInt::from(min_value) + 1) as BigInt; + let canonical_value = value + positive_range_fixer; + tempvar is_in_range; + hint TestLessThan {lhs: canonical_value, rhs: range_size} into {dst: is_in_range}; + jump IsInRange if is_in_range != 0; + tempvar is_overflow; + hint TestLessThan {lhs: value, rhs: range_size} into {dst: is_overflow}; + jump IsOverflow if is_overflow != 0; + // IsUnderflow: + // We need to assert that the value is smaller than the lower limit: + // value + 2**128 - min_value < 2**128 ==> value < min_value + const min_value_fixer = + (BigInt::from(u128::MAX) + 1 - BigInt::from(min_value)) as BigInt; + tempvar rc_val = value + min_value_fixer; + assert rc_val = *(range_check++); + let fixed_underflow = value + range_size; + jump Underflow; + IsOverflow: + // We need to assert that the value is larger than the upper limit: + // value - (max_value + 1) >= 0 ==> value > max_value + const max_value_plus_one = (BigInt::from(max_value) + 1) as BigInt; + tempvar rc_val = value - max_value_plus_one; + assert rc_val = *(range_check++); + let fixed_overflow = value - range_size; + jump Overflow; + IsInRange: + tempvar rc_val = canonical_value; + assert rc_val = *(range_check++); + } + // For i128, the previous range check already made sure the value is in range. + if min_value != i128::MIN || max_value != i128::MAX { + casm_build_extend! {casm_builder, + // value + 2**128 - max_value - 1 < 2**128 ==> value <= max_value + const fixer_limit = (BigInt::from(u128::MAX) - max_value) as BigInt; + tempvar rc_val = value + fixer_limit; + assert rc_val = *(range_check++); + }; + } + Ok(builder.build_from_casm_builder( + casm_builder, + [ + ("Fallthrough", &[&[range_check], &[value]], None), + ( + "Underflow", + &[&[range_check], &[fixed_underflow]], + Some(*underflow_handle_statement_id), + ), + ("Overflow", &[&[range_check], &[fixed_overflow]], Some(*overflow_handle_statement_id)), + ], + CostValidationInfo { + range_check_info: Some((orig_range_check, range_check)), + extra_costs: None, + }, + )) +} + /// Builds instructions for Sierra i8/i16/i32/i64 operations. pub fn build_sint< TSintTraits: SintTraits + IntMulTraits + IsZeroTraits, @@ -91,5 +185,8 @@ pub fn build_sint< SintConcrete::FromFelt252(_) => build_sint_from_felt252(builder, MIN_VALUE, MAX_VALUE), SintConcrete::IsZero(_) => misc::build_is_zero(builder), SintConcrete::WideMul(_) => build_small_wide_mul(builder), + SintConcrete::Operation(libfunc) => { + build_sint_overflowing_operation(builder, MIN_VALUE, MAX_VALUE, libfunc.operator) + } } } 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 050c3f01065..1f7f71dc0e0 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,6 +1,6 @@ use cairo_lang_sierra::extensions::int::signed128::Sint128Concrete; -use super::signed::build_sint_from_felt252; +use super::signed::{build_sint_from_felt252, build_sint_overflowing_operation}; use super::{build_const, CompiledInvocation, CompiledInvocationBuilder, InvocationError}; use crate::invocations::misc; @@ -15,5 +15,8 @@ pub fn build( Sint128Concrete::FromFelt252(_) => build_sint_from_felt252(builder, i128::MIN, i128::MAX), Sint128Concrete::ToFelt252(_) => misc::build_identity(builder), Sint128Concrete::Equal(_) => misc::build_cell_eq(builder), + Sint128Concrete::Operation(libfunc) => { + build_sint_overflowing_operation(builder, i128::MIN, i128::MAX, libfunc.operator) + } } } diff --git a/crates/cairo-lang-sierra/src/extensions/modules/int/mod.rs b/crates/cairo-lang-sierra/src/extensions/modules/int/mod.rs index 1000d25c391..0b3f84852e2 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/int/mod.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/int/mod.rs @@ -29,6 +29,16 @@ pub enum IntOperator { OverflowingSub, } +pub struct IntOperationConcreteLibfunc { + pub operator: IntOperator, + pub signature: LibfuncSignature, +} +impl SignatureBasedConcreteLibfunc for IntOperationConcreteLibfunc { + fn signature(&self) -> &LibfuncSignature { + &self.signature + } +} + /// Trait for implementing integers. pub trait IntTraits: Default { /// The rust matching type to this type. 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 63905a46464..37e69fed014 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/int/signed.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/int/signed.rs @@ -1,15 +1,29 @@ +use std::marker::PhantomData; + use super::signed128::Sint128Type; use super::{ - IntConstLibfunc, IntEqualLibfunc, IntFromFelt252Libfunc, IntMulTraits, IntToFelt252Libfunc, - IntTraits, IntType, IntWideMulLibfunc, + IntConstLibfunc, IntEqualLibfunc, IntFromFelt252Libfunc, IntMulTraits, + IntOperationConcreteLibfunc, IntOperator, IntToFelt252Libfunc, IntTraits, IntType, + IntWideMulLibfunc, }; use crate::define_libfunc_hierarchy; use crate::extensions::is_zero::{IsZeroLibfunc, IsZeroTraits}; -use crate::extensions::{GenericLibfunc, NamedType}; -use crate::ids::GenericTypeId; +use crate::extensions::lib_func::{ + BranchSignature, DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature, + SierraApChange, SignatureSpecializationContext, SpecializationContext, +}; +use crate::extensions::range_check::RangeCheckType; +use crate::extensions::{GenericLibfunc, NamedType, OutputVarReferenceInfo, SpecializationError}; +use crate::ids::{GenericLibfuncId, GenericTypeId}; +use crate::program::GenericArg; /// Trait for implementing signed integers. -pub trait SintTraits: IntTraits {} +pub trait SintTraits: IntTraits { + /// The generic libfunc id for addition. + const OVERFLOWING_ADD: &'static str; + /// The generic libfunc id for subtraction. + const OVERFLOWING_SUB: &'static str; +} define_libfunc_hierarchy! { pub enum SintLibfunc { @@ -17,15 +31,105 @@ define_libfunc_hierarchy! { Equal(IntEqualLibfunc), ToFelt252(IntToFelt252Libfunc), FromFelt252(IntFromFelt252Libfunc), + Operation(SintOperationLibfunc), IsZero(IsZeroLibfunc), WideMul(IntWideMulLibfunc), }, SintConcrete } +/// Libfunc for integer operations. +pub struct SintOperationLibfunc { + pub operator: IntOperator, + _phantom: PhantomData, +} +impl SintOperationLibfunc { + const OVERFLOWING_ADD: &'static str = TSintTraits::OVERFLOWING_ADD; + const OVERFLOWING_SUB: &'static str = TSintTraits::OVERFLOWING_SUB; + fn new(operator: IntOperator) -> Option { + Some(Self { operator, _phantom: PhantomData::default() }) + } +} +impl GenericLibfunc for SintOperationLibfunc { + type Concrete = IntOperationConcreteLibfunc; + + fn supported_ids() -> Vec { + vec![ + GenericLibfuncId::from(Self::OVERFLOWING_ADD), + GenericLibfuncId::from(Self::OVERFLOWING_SUB), + ] + } + + fn by_id(id: &GenericLibfuncId) -> Option { + match id.0.as_str() { + id if id == Self::OVERFLOWING_ADD => Self::new(IntOperator::OverflowingAdd), + id if id == Self::OVERFLOWING_SUB => Self::new(IntOperator::OverflowingSub), + _ => None, + } + } + + fn specialize_signature( + &self, + context: &dyn SignatureSpecializationContext, + args: &[GenericArg], + ) -> Result { + if !args.is_empty() { + return Err(SpecializationError::WrongNumberOfGenericArgs); + } + let ty = context.get_concrete_type(TSintTraits::GENERIC_TYPE_ID, &[])?; + let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?; + + let ty_param = ParamSignature::new(ty.clone()); + let rc_output_info = OutputVarInfo::new_builtin(range_check_type.clone(), 0); + let wrapping_result_info = OutputVarInfo { + ty: ty.clone(), + ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic), + }; + Ok(LibfuncSignature { + param_signatures: vec![ + ParamSignature::new(range_check_type).with_allow_add_const(), + ty_param.clone(), + ty_param, + ], + branch_signatures: vec![ + BranchSignature { + vars: vec![ + rc_output_info.clone(), + OutputVarInfo { ty, ref_info: OutputVarReferenceInfo::SimpleDerefs }, + ], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + BranchSignature { + vars: vec![rc_output_info.clone(), wrapping_result_info.clone()], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + BranchSignature { + vars: vec![rc_output_info, wrapping_result_info], + ap_change: SierraApChange::Known { new_vars_only: false }, + }, + ], + fallthrough: Some(0), + }) + } + + fn specialize( + &self, + context: &dyn SpecializationContext, + args: &[GenericArg], + ) -> Result { + Ok(IntOperationConcreteLibfunc { + operator: self.operator, + signature: self.specialize_signature(context.upcast(), args)?, + }) + } +} + #[derive(Default)] pub struct Sint8Traits; -impl SintTraits for Sint8Traits {} +impl SintTraits for Sint8Traits { + const OVERFLOWING_ADD: &'static str = "i8_overflowing_add_impl"; + const OVERFLOWING_SUB: &'static str = "i8_overflowing_sub_impl"; +} impl IntTraits for Sint8Traits { type IntType = i8; @@ -55,7 +159,10 @@ pub type Sint8Concrete = ::Concrete; #[derive(Default)] pub struct Sint16Traits; -impl SintTraits for Sint16Traits {} +impl SintTraits for Sint16Traits { + const OVERFLOWING_ADD: &'static str = "i16_overflowing_add_impl"; + const OVERFLOWING_SUB: &'static str = "i16_overflowing_sub_impl"; +} impl IntTraits for Sint16Traits { type IntType = i16; @@ -85,7 +192,10 @@ pub type Sint16Concrete = ::Concrete; #[derive(Default)] pub struct Sint32Traits; -impl SintTraits for Sint32Traits {} +impl SintTraits for Sint32Traits { + const OVERFLOWING_ADD: &'static str = "i32_overflowing_add_impl"; + const OVERFLOWING_SUB: &'static str = "i32_overflowing_sub_impl"; +} impl IntTraits for Sint32Traits { type IntType = i32; @@ -115,7 +225,10 @@ pub type Sint32Concrete = ::Concrete; #[derive(Default)] pub struct Sint64Traits; -impl SintTraits for Sint64Traits {} +impl SintTraits for Sint64Traits { + const OVERFLOWING_ADD: &'static str = "i64_overflowing_add_impl"; + const OVERFLOWING_SUB: &'static str = "i64_overflowing_sub_impl"; +} impl IntTraits for Sint64Traits { type IntType = i64; 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 32eb9fd1e33..be196d6af03 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,4 @@ -use super::signed::SintTraits; +use super::signed::{SintOperationLibfunc, SintTraits}; use super::{ IntConstLibfunc, IntEqualLibfunc, IntFromFelt252Libfunc, IntToFelt252Libfunc, IntTraits, IntType, @@ -17,6 +17,7 @@ define_libfunc_hierarchy! { Const(IntConstLibfunc), ToFelt252(IntToFelt252Libfunc), FromFelt252(IntFromFelt252Libfunc), + Operation(SintOperationLibfunc), IsZero(IsZeroLibfunc), }, Sint128Concrete } @@ -24,7 +25,10 @@ define_libfunc_hierarchy! { #[derive(Default)] pub struct Sint128Traits; -impl SintTraits for Sint128Traits {} +impl SintTraits for Sint128Traits { + const OVERFLOWING_ADD: &'static str = "i128_overflowing_add_impl"; + const OVERFLOWING_SUB: &'static str = "i128_overflowing_sub_impl"; +} impl IntTraits for Sint128Traits { type IntType = i128; diff --git a/crates/cairo-lang-sierra/src/extensions/modules/int/unsigned.rs b/crates/cairo-lang-sierra/src/extensions/modules/int/unsigned.rs index ee5a30fb051..bc6266c91f5 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/int/unsigned.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/int/unsigned.rs @@ -2,8 +2,9 @@ use std::marker::PhantomData; use super::unsigned128::Uint128Type; use super::{ - IntConstLibfunc, IntEqualLibfunc, IntFromFelt252Libfunc, IntMulTraits, IntOperator, - IntToFelt252Libfunc, IntTraits, IntType, IntWideMulLibfunc, + IntConstLibfunc, IntEqualLibfunc, IntFromFelt252Libfunc, IntMulTraits, + IntOperationConcreteLibfunc, IntOperator, IntToFelt252Libfunc, IntTraits, IntType, + IntWideMulLibfunc, }; use crate::define_libfunc_hierarchy; use crate::extensions::bitwise::BitwiseType; @@ -16,7 +17,7 @@ use crate::extensions::non_zero::nonzero_ty; use crate::extensions::range_check::RangeCheckType; use crate::extensions::{ GenericLibfunc, NamedType, NoGenericArgsGenericLibfunc, OutputVarReferenceInfo, - SignatureBasedConcreteLibfunc, SpecializationError, + SpecializationError, }; use crate::ids::{GenericLibfuncId, GenericTypeId}; use crate::program::GenericArg; @@ -37,16 +38,6 @@ pub trait UintTraits: IntTraits { const BITWISE: &'static str; } -pub struct UintOperationConcreteLibfunc { - pub operator: IntOperator, - pub signature: LibfuncSignature, -} -impl SignatureBasedConcreteLibfunc for UintOperationConcreteLibfunc { - fn signature(&self) -> &LibfuncSignature { - &self.signature - } -} - /// Libfunc for integer operations. pub struct UintOperationLibfunc { pub operator: IntOperator, @@ -60,7 +51,7 @@ impl UintOperationLibfunc { } } impl GenericLibfunc for UintOperationLibfunc { - type Concrete = UintOperationConcreteLibfunc; + type Concrete = IntOperationConcreteLibfunc; fn supported_ids() -> Vec { vec![ @@ -133,7 +124,7 @@ impl GenericLibfunc for UintOperationLibfunc Result { - Ok(UintOperationConcreteLibfunc { + Ok(IntOperationConcreteLibfunc { operator: self.operator, signature: self.specialize_signature(context.upcast(), args)?, }) 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 2676a2d62be..1159f2685cc 100644 --- a/crates/cairo-lang-starknet/src/allowed_libfuncs_lists/all.json +++ b/crates/cairo-lang-starknet/src/allowed_libfuncs_lists/all.json @@ -70,29 +70,39 @@ "i128_const", "i128_eq", "i128_is_zero", + "i128_overflowing_add_impl", + "i128_overflowing_sub_impl", "i128_to_felt252", "i128_try_from_felt252", "i16_const", "i16_eq", "i16_is_zero", + "i16_overflowing_add_impl", + "i16_overflowing_sub_impl", "i16_to_felt252", "i16_try_from_felt252", "i16_wide_mul", "i32_const", "i32_eq", "i32_is_zero", + "i32_overflowing_add_impl", + "i32_overflowing_sub_impl", "i32_to_felt252", "i32_try_from_felt252", "i32_wide_mul", "i64_const", "i64_eq", "i64_is_zero", + "i64_overflowing_add_impl", + "i64_overflowing_sub_impl", "i64_to_felt252", "i64_try_from_felt252", "i64_wide_mul", "i8_const", "i8_eq", "i8_is_zero", + "i8_overflowing_add_impl", + "i8_overflowing_sub_impl", "i8_to_felt252", "i8_try_from_felt252", "i8_wide_mul", diff --git a/tests/e2e_test_data/libfuncs/i128 b/tests/e2e_test_data/libfuncs/i128 index 1e80d3c427a..55a1f23e017 100644 --- a/tests/e2e_test_data/libfuncs/i128 +++ b/tests/e2e_test_data/libfuncs/i128 @@ -226,6 +226,166 @@ test::foo@0() -> (core::bool); //! > ========================================================================== +//! > i128_overflowing_add_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i128, b: i128) -> integer::SignedIntegerResult { + integer::i128_overflowing_add_impl(a, b) +} + +//! > casm +[ap + 0] = [fp + -4] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 170141183460469231731687303715884105728) % PRIME < 340282366920938463463374607431768211456 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 340282366920938463463374607431768211456 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 510423550381407695195061911147652317184, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 19; +[ap + -3] = [ap + 0] + 170141183460469231731687303715884105728, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 22; +[ap + 0] = [ap + -2] + 170141183460469231731687303715884105728, ap++; +[ap + -1] = [[fp + -5] + 0]; +ap += 1; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 340282366920938463463374607431768211456, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 340282366920938463463374607431768211456, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1070}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i128 = i128; +type core::integer::SignedIntegerResult:: = Enum, i128, i128, i128>; + +libfunc i128_overflowing_add_impl = i128_overflowing_add_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i128_overflowing_add_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i128, [2]: i128) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + +//! > i128_overflowing_sub_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i128, b: i128) -> integer::SignedIntegerResult { + integer::i128_overflowing_sub_impl(a, b) +} + +//! > casm +[fp + -4] = [ap + 0] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 170141183460469231731687303715884105728) % PRIME < 340282366920938463463374607431768211456 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 340282366920938463463374607431768211456 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 510423550381407695195061911147652317184, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 19; +[ap + -3] = [ap + 0] + 170141183460469231731687303715884105728, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 22; +[ap + 0] = [ap + -2] + 170141183460469231731687303715884105728, ap++; +[ap + -1] = [[fp + -5] + 0]; +ap += 1; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 340282366920938463463374607431768211456, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 340282366920938463463374607431768211456, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1070}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i128 = i128; +type core::integer::SignedIntegerResult:: = Enum, i128, i128, i128>; + +libfunc i128_overflowing_sub_impl = i128_overflowing_sub_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i128_overflowing_sub_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i128, [2]: i128) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + //! > i128_is_zero libfunc //! > test_runner_name diff --git a/tests/e2e_test_data/libfuncs/i16 b/tests/e2e_test_data/libfuncs/i16 index 2588d29ce2d..00d86b0189c 100644 --- a/tests/e2e_test_data/libfuncs/i16 +++ b/tests/e2e_test_data/libfuncs/i16 @@ -228,6 +228,168 @@ test::foo@0() -> (core::bool); //! > ========================================================================== +//! > i16_overflowing_add_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i16, b: i16) -> integer::SignedIntegerResult { + integer::i16_overflowing_add_impl(a, b) +} + +//! > casm +[ap + 0] = [fp + -4] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 32768) % PRIME < 65536 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 65536 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768244224, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 32768, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 32768, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768178688, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 65536, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 65536, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i16 = i16; +type core::integer::SignedIntegerResult:: = Enum, i16, i16, i16>; + +libfunc i16_overflowing_add_impl = i16_overflowing_add_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i16_overflowing_add_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i16, [2]: i16) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + +//! > i16_overflowing_sub_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i16, b: i16) -> integer::SignedIntegerResult { + integer::i16_overflowing_sub_impl(a, b) +} + +//! > casm +[fp + -4] = [ap + 0] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 32768) % PRIME < 65536 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 65536 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768244224, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 32768, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 32768, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768178688, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 65536, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 65536, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i16 = i16; +type core::integer::SignedIntegerResult:: = Enum, i16, i16, i16>; + +libfunc i16_overflowing_sub_impl = i16_overflowing_sub_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i16_overflowing_sub_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i16, [2]: i16) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + //! > i16_is_zero libfunc //! > test_runner_name diff --git a/tests/e2e_test_data/libfuncs/i32 b/tests/e2e_test_data/libfuncs/i32 index 9574a739f17..d0665f031a0 100644 --- a/tests/e2e_test_data/libfuncs/i32 +++ b/tests/e2e_test_data/libfuncs/i32 @@ -228,6 +228,168 @@ test::foo@0() -> (core::bool); //! > ========================================================================== +//! > i32_overflowing_add_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i32, b: i32) -> integer::SignedIntegerResult { + integer::i32_overflowing_add_impl(a, b) +} + +//! > casm +[ap + 0] = [fp + -4] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 2147483648) % PRIME < 4294967296 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 4294967296 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463463374607433915695104, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 2147483648, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 2147483648, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463463374607429620727808, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 4294967296, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 4294967296, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i32 = i32; +type core::integer::SignedIntegerResult:: = Enum, i32, i32, i32>; + +libfunc i32_overflowing_add_impl = i32_overflowing_add_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i32_overflowing_add_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i32, [2]: i32) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + +//! > i32_overflowing_sub_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i32, b: i32) -> integer::SignedIntegerResult { + integer::i32_overflowing_sub_impl(a, b) +} + +//! > casm +[fp + -4] = [ap + 0] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 2147483648) % PRIME < 4294967296 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 4294967296 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463463374607433915695104, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 2147483648, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 2147483648, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463463374607429620727808, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 4294967296, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 4294967296, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i32 = i32; +type core::integer::SignedIntegerResult:: = Enum, i32, i32, i32>; + +libfunc i32_overflowing_sub_impl = i32_overflowing_sub_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i32_overflowing_sub_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i32, [2]: i32) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + //! > i32_is_zero libfunc //! > test_runner_name diff --git a/tests/e2e_test_data/libfuncs/i64 b/tests/e2e_test_data/libfuncs/i64 index b3da16fe24b..ad696c6101f 100644 --- a/tests/e2e_test_data/libfuncs/i64 +++ b/tests/e2e_test_data/libfuncs/i64 @@ -228,6 +228,168 @@ test::foo@0() -> (core::bool); //! > ========================================================================== +//! > i64_overflowing_add_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i64, b: i64) -> integer::SignedIntegerResult { + integer::i64_overflowing_add_impl(a, b) +} + +//! > casm +[ap + 0] = [fp + -4] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 9223372036854775808) % PRIME < 18446744073709551616 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 18446744073709551616 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463472597979468622987264, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 9223372036854775808, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 9223372036854775808, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463454151235394913435648, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 18446744073709551616, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 18446744073709551616, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i64 = i64; +type core::integer::SignedIntegerResult:: = Enum, i64, i64, i64>; + +libfunc i64_overflowing_add_impl = i64_overflowing_add_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i64_overflowing_add_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i64, [2]: i64) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + +//! > i64_overflowing_sub_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i64, b: i64) -> integer::SignedIntegerResult { + integer::i64_overflowing_sub_impl(a, b) +} + +//! > casm +[fp + -4] = [ap + 0] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 9223372036854775808) % PRIME < 18446744073709551616 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 18446744073709551616 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463472597979468622987264, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 9223372036854775808, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 9223372036854775808, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463454151235394913435648, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 18446744073709551616, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 18446744073709551616, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i64 = i64; +type core::integer::SignedIntegerResult:: = Enum, i64, i64, i64>; + +libfunc i64_overflowing_sub_impl = i64_overflowing_sub_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i64_overflowing_sub_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i64, [2]: i64) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + //! > i64_is_zero libfunc //! > test_runner_name diff --git a/tests/e2e_test_data/libfuncs/i8 b/tests/e2e_test_data/libfuncs/i8 index 61cd6356d28..7ace96b6651 100644 --- a/tests/e2e_test_data/libfuncs/i8 +++ b/tests/e2e_test_data/libfuncs/i8 @@ -228,6 +228,168 @@ test::foo@0() -> (core::bool); //! > ========================================================================== +//! > i8_overflowing_add_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i8, b: i8) -> integer::SignedIntegerResult { + integer::i8_overflowing_add_impl(a, b) +} + +//! > casm +[ap + 0] = [fp + -4] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 128) % PRIME < 256 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 256 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768211584, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 128, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 128, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768211328, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 256, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 256, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i8 = i8; +type core::integer::SignedIntegerResult:: = Enum, i8, i8, i8>; + +libfunc i8_overflowing_add_impl = i8_overflowing_add_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i8_overflowing_add_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i8, [2]: i8) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + +//! > i8_overflowing_sub_impl libfunc + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo +fn foo(a: i8, b: i8) -> integer::SignedIntegerResult { + integer::i8_overflowing_sub_impl(a, b) +} + +//! > casm +[fp + -4] = [ap + 0] + [fp + -3], ap++; +%{ memory[ap + 0] = (memory[ap + -1] + 128) % PRIME < 256 %} +jmp rel 14 if [ap + 0] != 0, ap++; +%{ memory[ap + 0] = memory[ap + -2] < 256 %} +jmp rel 7 if [ap + 0] != 0, ap++; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768211584, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 20; +[ap + -3] = [ap + 0] + 128, ap++; +[ap + -1] = [[fp + -5] + 0]; +jmp rel 23; +[ap + 0] = [ap + -2] + 128, ap++; +[ap + -1] = [[fp + -5] + 0]; +[ap + 0] = [ap + -3] + 340282366920938463463374607431768211328, ap++; +[ap + -1] = [[fp + -5] + 1]; +[ap + 0] = [fp + -5] + 2, ap++; +[ap + 0] = 5, ap++; +[ap + 0] = [ap + -6], ap++; +jmp rel 16; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 3, ap++; +[ap + 0] = [ap + -6] + 256, ap++; +jmp rel 8; +[ap + 0] = [fp + -5] + 1, ap++; +[ap + 0] = 1, ap++; +[ap + -6] = [ap + 0] + 256, ap++; +ret; + +//! > function_costs +test::foo: OrderedHashMap({Const: 1140}) + +//! > sierra_code +type RangeCheck = RangeCheck; +type i8 = i8; +type core::integer::SignedIntegerResult:: = Enum, i8, i8, i8>; + +libfunc i8_overflowing_sub_impl = i8_overflowing_sub_impl; +libfunc branch_align = branch_align; +libfunc enum_init, 0> = enum_init, 0>; +libfunc store_temp = store_temp; +libfunc store_temp> = store_temp>; +libfunc jump = jump; +libfunc enum_init, 1> = enum_init, 1>; +libfunc enum_init, 2> = enum_init, 2>; +libfunc rename = rename; +libfunc rename> = rename>; + +i8_overflowing_sub_impl([0], [1], [2]) { fallthrough([3], [4]) 6([5], [6]) 11([7], [8]) }; +branch_align() -> (); +enum_init, 0>([4]) -> ([9]); +store_temp([3]) -> ([10]); +store_temp>([9]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 1>([6]) -> ([12]); +store_temp([5]) -> ([10]); +store_temp>([12]) -> ([11]); +jump() { 15() }; +branch_align() -> (); +enum_init, 2>([8]) -> ([13]); +store_temp([7]) -> ([10]); +store_temp>([13]) -> ([11]); +rename([10]) -> ([14]); +rename>([11]) -> ([15]); +return([14], [15]); + +test::foo@0([0]: RangeCheck, [1]: i8, [2]: i8) -> (RangeCheck, core::integer::SignedIntegerResult::); + +//! > ========================================================================== + //! > i8_is_zero libfunc //! > test_runner_name